Skip to content

Commit f15465f

Browse files
acarl005oz-agent
andcommitted
Log Windows shell spawn parameters on failure
Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent e7ff8af commit f15465f

1 file changed

Lines changed: 101 additions & 11 deletions

File tree

  • app/src/terminal/local_tty/windows

app/src/terminal/local_tty/windows/mod.rs

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -173,27 +173,34 @@ pub(super) fn spawn(
173173
};
174174
let mut process_information = PROCESS_INFORMATION::default();
175175

176-
let start_directory = options
176+
let start_directory_path = options
177177
.start_dir
178178
.filter(|start_dir| start_dir.is_dir())
179179
.or_else(|| {
180180
std::env::var_os("USERPROFILE")
181181
.map(PathBuf::from)
182182
.filter(|path| path.is_dir())
183-
})
183+
});
184+
let start_directory_wide = start_directory_path
185+
.as_ref()
186+
.map(|path| path.as_os_str().encode_wide().collect::<Vec<u16>>());
187+
let start_directory = start_directory_path
188+
.as_ref()
184189
.map(|path| HSTRING::from(path.as_os_str()));
185190

191+
let creation_flags = PROCESS_CREATION_FLAGS(0)
192+
| EXTENDED_STARTUPINFO_PRESENT
193+
| CREATE_UNICODE_ENVIRONMENT
194+
| CREATE_BREAKAWAY_FROM_JOB;
195+
186196
unsafe {
187197
CreateProcessW(
188198
PCWSTR::null(), /* lpApplicationName */
189-
Some(PWSTR::from_raw(shell_command.as_ptr().cast_mut())),
199+
Some(PWSTR::from_raw(shell_command.raw.as_ptr().cast_mut())),
190200
None, /* lpProcessAttributes */
191201
None, /* lpThreadAttributes */
192202
false, /* bInheritHandles */
193-
PROCESS_CREATION_FLAGS(0)
194-
| EXTENDED_STARTUPINFO_PRESENT
195-
| CREATE_UNICODE_ENVIRONMENT
196-
| CREATE_BREAKAWAY_FROM_JOB,
203+
creation_flags,
197204
Some(environment_block.as_ptr() as *const std::ffi::c_void),
198205
start_directory
199206
.as_ref()
@@ -204,6 +211,17 @@ pub(super) fn spawn(
204211
)
205212
.map_err(|error| {
206213
let detail = shell_starter.shell_detail();
214+
log_create_process_failure(
215+
&error,
216+
&detail,
217+
&shell_command,
218+
&environment_block,
219+
start_directory_wide.as_deref(),
220+
creation_flags,
221+
&startup_info,
222+
size.columns,
223+
size.rows,
224+
);
207225
PtySpawnError::CreateShellProcessFailed { detail, error }
208226
})?;
209227
}
@@ -232,7 +250,23 @@ pub enum EncodingError {
232250
Other(#[from] windows::core::Error),
233251
}
234252

235-
fn wsl_shell_command(wsl_shell_starter: &WslShellStarter) -> Result<HSTRING, EncodingError> {
253+
struct EncodedShellCommand {
254+
raw: HSTRING,
255+
wide_units: Vec<u16>,
256+
}
257+
258+
impl EncodedShellCommand {
259+
fn new(wide_units: Vec<u16>) -> Self {
260+
Self {
261+
raw: HSTRING::from_wide(&wide_units),
262+
wide_units,
263+
}
264+
}
265+
}
266+
267+
fn wsl_shell_command(
268+
wsl_shell_starter: &WslShellStarter,
269+
) -> Result<EncodedShellCommand, EncodingError> {
236270
let mut encoded_shell_command = Vec::<u16>::new();
237271

238272
log::info!(
@@ -247,14 +281,14 @@ fn wsl_shell_command(wsl_shell_starter: &WslShellStarter) -> Result<HSTRING, Enc
247281
}
248282
append_quoted(arg, &mut encoded_shell_command);
249283
}
250-
Ok(HSTRING::from_wide(&encoded_shell_command))
284+
Ok(EncodedShellCommand::new(encoded_shell_command))
251285
}
252286

253287
/// Constructs the shell command in a Windows-native encoding using 16-bit values.
254288
///
255289
/// Windows expects a single string for its command which includes all arguments, so we need to
256290
/// string them together and escape them appropriately.
257-
fn shell_command(shell_starter: &DirectShellStarter) -> Result<HSTRING, EncodingError> {
291+
fn shell_command(shell_starter: &DirectShellStarter) -> Result<EncodedShellCommand, EncodingError> {
258292
let mut encoded_shell_command = Vec::<u16>::new();
259293

260294
log::info!(
@@ -273,7 +307,63 @@ fn shell_command(shell_starter: &DirectShellStarter) -> Result<HSTRING, Encoding
273307
}
274308
append_quoted(arg, &mut encoded_shell_command);
275309
}
276-
Ok(HSTRING::from_wide(&encoded_shell_command))
310+
Ok(EncodedShellCommand::new(encoded_shell_command))
311+
}
312+
313+
fn log_create_process_failure(
314+
error: &windows::core::Error,
315+
detail: &str,
316+
shell_command: &EncodedShellCommand,
317+
environment_block: &[u16],
318+
start_directory: Option<&[u16]>,
319+
creation_flags: PROCESS_CREATION_FLAGS,
320+
startup_info: &STARTUPINFOEXW,
321+
columns: usize,
322+
rows: usize,
323+
) {
324+
let shell_command_bytes = wide_units_as_le_bytes_with_nul(&shell_command.wide_units, 1);
325+
let environment_block_bytes = wide_units_as_le_bytes(environment_block);
326+
let start_directory_bytes = start_directory
327+
.map(|wide| wide_units_as_le_bytes_with_nul(wide, 1))
328+
.unwrap_or_default();
329+
330+
log::error!(
331+
"CreateProcessW failed for shell process {detail}: {error:#}. \
332+
debug lpApplicationName_is_null=true \
333+
lpCommandLine_utf16_units_len={} lpCommandLine_bytes={:?} \
334+
lpEnvironment_utf16_units_len={} lpEnvironment_bytes={:?} \
335+
lpCurrentDirectory_is_null={} lpCurrentDirectory_utf16_units_len={} lpCurrentDirectory_bytes={:?} \
336+
dwCreationFlags={:#010x} bInheritHandles=false \
337+
startup_cb={} startup_dwFlags={:#010x} startup_lpAttributeList_is_null={} \
338+
conpty_size_columns={} conpty_size_rows={}",
339+
shell_command.wide_units.len() + 1,
340+
shell_command_bytes,
341+
environment_block.len(),
342+
environment_block_bytes,
343+
start_directory.is_none(),
344+
start_directory.map_or(0, |wide| wide.len() + 1),
345+
start_directory_bytes,
346+
creation_flags.0,
347+
startup_info.StartupInfo.cb,
348+
startup_info.StartupInfo.dwFlags.0,
349+
startup_info.lpAttributeList.is_null(),
350+
columns,
351+
rows,
352+
);
353+
}
354+
355+
fn wide_units_as_le_bytes(wide_units: &[u16]) -> Vec<u8> {
356+
wide_units
357+
.iter()
358+
.flat_map(|unit| unit.to_le_bytes())
359+
.collect()
360+
}
361+
362+
fn wide_units_as_le_bytes_with_nul(wide_units: &[u16], trailing_nuls: usize) -> Vec<u8> {
363+
let mut bytes = Vec::with_capacity((wide_units.len() + trailing_nuls) * 2);
364+
bytes.extend(wide_units_as_le_bytes(wide_units));
365+
bytes.resize(bytes.len() + trailing_nuls * 2, 0);
366+
bytes
277367
}
278368

279369
/// Appends an argument and properly quotes it.

0 commit comments

Comments
 (0)