Perl / perl5

đŸȘ The Perl programming language
https://dev.perl.org/perl5/
Other
1.96k stars 555 forks source link

system() on Win32 tries to launch bogus paths+bad cmd parsing #13614

Open p5pRT opened 10 years ago

p5pRT commented 10 years ago

Migrated from rt.perl.org#121283 (status was 'open')

Searchable as RT121283$

p5pRT commented 10 years ago

From @bulk88

Created by @bulk88

While investigating why re/subst.t and other subst*.t tests take 300 seconds on Win32 while their watchdog process times out\, (which is because the kill() in the end block is on cmd.exe's PID\, not the watchdog perl's PID\, killing the cmd.exe leaves watchdog perl process around\, which blocks harness script until the timeout at as high as 300 seconds expires\, why harness blocks on the watchdog proc IDK\, and that is for another ticket\, if appropriate) and other watchdog() from test.pl using .ts. TonyC brought up the question of why system() made a cmd.exe process when perl is not a shell builtin and should have been directly launched by the parent perl process (a perl that called watchdog()). I will investigate that in this ticket. This ticket isn't directly about the watchdog() bug\, it is about system()'s behavior. Fixing system() will probably fix the watchdog bug (there are other ways too\, like investigating why harness process hangs on the watchdog\, and switching test.pl to a process group kill() instead of single process kill()\, process group kill on Win32 was broken in 5.17 and is another ticket at https://rt-archive.perl.org/perl5/Ticket/Display.html?id=121230).

For testing my script is ------------------------------------------------------ system( 1\, 'C​:\\perl519\\src\\t\\perl.exe "-I../lib" -e "sleep(300);warn qq/# Tes t process timed out - terminating\\n/;kill(KILL\, 12748);" '); ----------------------------------------------------- I am not sure why the "\\" are there even though its single quote. Data​::Dumper printed it that way. PID 12748 doesn't exist on my system but it doesn't matter.

The callstack on blead generally looks like -------------------------------------------------------

perl519.dll!do_spawnvp_handles(int mode=1\, const char * cmdname=0x0090c5ec\, const char * const * argv=0x008f5614\, const int * handles=0x00000000) Line 3705 C perl519.dll!win32_spawnvp(int mode=1\, const char * cmdname=0x0090c5ec\, const char * const * argv=0x008f5614) Line 3698 + 0x13 C perl519.dll!Perl_do_aspawn(interpreter * my_perl=0x003645ec\, sv * really=0x00000000\, sv * * mark=0x0036aec8\, sv * * sp=0x0036aec4) Line 644 + 0x5b C perl519.dll!Perl_pp_system(interpreter * my_perl=0x003645ec) Line 4225 + 0x13 C perl519.dll!Perl_runops_debug(interpreter * my_perl=0x003645ec) Line 2420 + 0xd C perl519.dll!S_run_body(interpreter * my_perl=0x003645ec\, long oldscope=1) Line 2446 + 0xd C perl519.dll!perl_run(interpreter * my_perl=0x003645ec) Line 2365 C perl519.dll!RunPerl(int argc=2\, char * * argv=0x00362478\, char * * env=0x003629e0) Line 270 + 0x9 C++ perl.exe!main(int argc=2\, char * * argv=0x00362478\, char * * env=0x00362d58) Line 23 + 0x12 C perl.exe!mainCRTStartup() Line 398 + 0xe C kernel32.dll!_BaseProcessStart@​4() + 0x23 --------------------------------------------------------- win32_spawnvp is a wrapper that has no logic of its own nowadays so I will ignore it exists. On the 1st try in do_spawnvp_handles CreateProcess is given "C​:\perl519\src\t\perl.exe -I../lib -e sleep(300);warn qq/# Test process timed out - terminating\n/;kill(KILL\, 3824);" (no outer quotes) as cname/LPCTSTR lpApplicationName\, also notice all `"`s were removed from inside the path by perl\, and "C​:\perl519\src\t\perl.exe "-I../lib" -e "sleep(300);warn qq/# Test process timed out - terminating\n/;kill(KILL\, 3824);"" (no outer quotes) as cmd/LPTSTR lpCommandLine.

lpApplicationName's MS docs are ------------------------------------------------------ lpApplicationName [in] Pointer to a null-terminated string that specifies the module to execute. The specified module can be a Windows-based application. It can be some other type of module (for example\, MS-DOS or OS/2) if the appropriate subsystem is available on the local computer. The string can specify the full path and file name of the module to execute or it can specify a partial name. In the case of a partial name\, the function uses the current drive and current directory to complete the specification. The function will not use the search path. If the file name does not contain an extension\, .exe is assumed. Therefore\, if the file name extension is .com\, this parameter must include the .com extension. The lpApplicationName parameter can be NULL. In that case\, the module name must be the first white space-delimited token in the lpCommandLine string. If you are using a long file name that contains a space\, use quoted strings to indicate where the file name ends and the arguments begin; otherwise\, the file name is ambiguous. For example\, consider the string "c​:\program files\sub dir\program name". This string can be interpreted in a number of ways. The system tries to interpret the possibilities in the following order​: c​:\program.exe files\sub dir\program name c​:\program files\sub.exe dir\program name c​:\program files\sub dir\program.exe name c​:\program files\sub dir\program name.exe If the executable module is a 16-bit application\, lpApplicationName should be NULL\, and the string pointed to by lpCommandLine should specify the executable module as well as its arguments. To run a batch file\, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the name of the batch file. ------------------------------------------------------

Obviously c string "C​:\perl519\src\t\perl.exe -I../lib -e sleep(300);warn qq/# Test process timed out - terminating\n/;kill(KILL\, 3824);" is not a file. The 1st CreateProcess fails with GLR == 3/ERROR_PATH_NOT_FOUND. This is a reasonable error. Then do_spawnvp_handles() calls qualified_path() is called on this bogus file path. GetFileAttributes is called on file "C​:\perl519\src\t\perl.exe -I../lib -e sleep(300);warn qq/# Test process timed out - terminating\n/;kill(KILL\, 3824); .exe"\, notice the new ".exe" at the end. GetFileAttributes fails with GLR == 3/ERROR_PATH_NOT_FOUND. Then GetFileAttributes is called with "C​:\perl519\src\t\perl.exe -I../lib -e sleep(300);warn qq/# Test process timed out - terminating\n/;kill(KILL\, 3824);". Another bogus file. qualified_path() fails by returning NULL at this point. Then in do_spawnvp_handles\, the following executes ------------------------------- errno = ENOENT; ret = -1; goto RETVAL; ------------------------ And control returns to Perl_do_aspawn in win32.c\, do_aspawn then adds cmd.exe to argv array and then calls do_spawnvp_handles again with "cmd.exe" as cmdname instead of a bogus command line string. This works. But the child proc is now wrapped in a cmd.exe process. system()'s docs say ---------------------------------------- Does exactly the same thing as exec LIST \, except that a fork is done first and the parent process waits for the child process to exit. Note that argument processing varies depending on the number of arguments. If there is more than one argument in LIST\, or if LIST is an array with more than one value\, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument\, the argument is checked for shell metacharacters\, and if there are any\, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms\, but varies on other platforms). If there are no shell metacharacters in the argument\, it is split into words and passed directly to execvp \, which is more efficient. ---------------------------------------- The 2 parts that I dont fully understand is “the argument is checked for shell metacharacters” and “if there are no shell metacharacters in the argument”. We have a complete command line. The file path name might being with a “ or not. The file path name might have a space in it\, and use “ to group it into a file name. IDK what this code does Unix. There is a Perl_do_exec3 which defines “check for shell metacharacters” but I dont think it is used on Windows. IDK what system() should be doing\, TonyC says it should not be calling cmd.exe/the shell. The docs say 1 arg list to system() is always a shell. Win32 Perl uselessly trys to launch the process without shell currently\, which needs to be stopped. Either Win32 Perl always uses shell/cmd.exe for 1 item system(). Or Perl knows how to pull out the file path name from the string and pass that to CreateProcess. IDK which it should be.

Perl Info ``` Flags: category=core severity=low Site configuration information for perl 5.19.9: Configured by Owner at Wed Feb 12 06:47:30 2014. Summary of my perl5 (revision 5 version 19 subversion 9) configuration: Derived from: 633f0fd2ca244ca83cc99b3af3a7d3ac2931850b Platform: osname=MSWin32, osvers=5.1, archname=MSWin32-x86-multi-thread uname='' config_args='undef' hint=recommended, useposix=true, d_sigaction=undef useithreads=define, usemultiplicity=define use64bitint=undef, use64bitall=undef, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cl', ccflags ='-nologo -GF -W3 -Od -MD -Zi -DDEBUGGING -DWIN32 -D_CONSOLE -DNO_STRICT -DPERL_TEXTMODE_SCRIPTS -DPERL_HASH_FUNC_ONE_AT_A_TIME -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DUSE_PERLIO -D_USE_32BIT_TIME_T', optimize='-Od -MD -Zi -DDEBUGGING', cppflags='-DWIN32' ccversion='13.10.6030', gccversion='', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=8 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='__int64', lseeksize=8 alignbytes=8, prototype=define Linker and Libraries: ld='link', ldflags ='-nologo -nodefaultlib -debug -libpath:"c:\perl519\lib\CORE" -machine:x86' libpth="C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\lib" libs=oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib perllibs=oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib libc=msvcrt.lib, so=dll, useshrplib=true, libperl=perl519.lib gnulibc_version='' Dynamic Linking: dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' ' cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib -debug -libpath:"c:\perl519\lib\CORE" -machine:x86' Locally applied patches: uncommitted-changes @INC for perl 5.19.9: C:/perl519/site/lib C:/perl519/lib . Environment for perl 5.19.9: HOME (unset) LANG (unset) LANGUAGE (unset) LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=C:\perl519\bin;C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE;C:\Program Files\Microsoft Visual Studio .NET 2003\VC7\BIN;C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools;C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin\prerelease;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\wbem; PERL_BADLANG (unset) SHELL (unset) ```
p5pRT commented 10 years ago

From @ikegami

On Thu\, Feb 20\, 2014 at 12​:46 AM\, bulk88 \perlbug\-followup@​perl\.org wrote​:

The 2 parts that I dont fully understand is "the argument is checked for shell metacharacters" and "if there are no shell metacharacters in the argument".

It means "if we can easily execute the shell command without invoking a shell".

p5pRT commented 10 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 10 years ago

From @bulk88

ideas from IRC


[16​:14] \<@​xdg> bulk88\, I'd suggest avoiding the quoted single quote\, too [16​:14] \<@​xdg> s/wasn't/not/ and see what happnes [16​:15] \ xdg​: haha\, right :D [16​:16] \<@​bulk88> "system(1\, $^X\, '-e'\, 'sleep 10; print qq[whatever\n]');" that works [16​:16] \<@​bulk88> should perl system() escape all "s it finds in list args mode? [16​:16] \ bulk88​: basically\, you can't have "s anywhere [16​:16] \ don't rely on that [16​:16] \<@​xdg> https://gist.github.com/dagolden/9238751 [16​:17] \<+dipsy> [ gist​:9238751 ] [16​:17] \ that quoting is incomplete and unreliable for all but the most simple cases [16​:17] \<@​xdg> bulk88\, see my gist [16​:17] \<@​xdg> that worked for me [16​:17] \<@​bulk88> "Does exactly the same thing as exec LIST \, except that a fork is done first and the parent process waits for the child process to exit. Note that argument processing varies depending on the number of arguments. If there is more than one argument in LIST\, or if LIST is an array with more than one value\, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument\, the argument is checked for shell metacharacters\, and if there are any\, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms\, but varies on other platforms). If there are no shell metacharacters in the argument\, it is split into words and passed directly to execvp \, which is more efficient." [16​:18] \ bulk88​: what's your achieve here? [16​:18] \<@​xdg> bulk88\, as I said\, that's wrong. And even perlport doesn't clarify sufficiently [16​:18] \<@​bulk88> so its not a bug\, just undefined behavior? [16​:19] \ bulk88​: it does SOME quoting\, it's just really shitty and incomplete quoting [16​:19] \ don't bother with it [16​:19] \<@​xdg> My opinion is that it's probably a bug\, but no one understands windows shell quoting in Perl well enough to diagnose\, fix or document properly. :-( [16​:20] \ well\, there is a proper quoting algorithm\, it's just in perl [16​:20] \<@​bulk88> there is Perl_do_exec3 in doio.c but that is the correct logic\, but for nix shells [16​:20] \ https://metacpan.org/source/BINGOS/ExtUtils-MakeMaker-6.90/lib/ExtUtils/MM_Win32.pm#L497 [16​:20] \<+dipsy> [ lib/ExtUtils/MM_Win32.pm - metacpan.org ] [16​:21] \ that is the exact logic you mean [16​:21] \ and i don't think anyone wants to implement that in c [16​:21] \<@​xdg> I'll rephrase. I'm sure what perl does for system on windows is buggy\, but no one seems to be expert enough and motivated enough to fix it [16​:21] \<@​xdg> bulk88\, I think win32/win32.c has the Windows equivalents [16​:21] \<@​xdg> I've walked through it ages ago but am very rusty [16​:22] \<@​xdg> there's has_shell_metacharacters and such [16​:22] \<@​bulk88> the system/exec/backticks code is horrible in win32.c\, I stepped it recently\, and gave up since IDK what is correct and wrong behavior in it [16​:22] \<@​bulk88> see https://rt-archive.perl.org/perl5/Ticket/Display.html?id=121283 [16​:22] \<@​xdg> exactly. motivation and expertise are not in the same person [16​:23] \<+dipsy> [ Bug #121283 for perl5​: system() on Win32 tries to launch bogus paths+bad cmd parsing ] [16​:23] \ bulk88​: as i said\, the MM_Win32.pm code linked above is correct\, and the system() code is definitely incorrect [16​:23] \<@​xdg> filing tickets like that is a good way to make sure it doesn't get lost .................cut....... [16​:23] \<@​xdg> Mithaldu\, I'm not convinced that MM_Win32 would necessarily work with system(1\,...) either [16​:23] \ xdg​: how so? [16​:25] \ oh right\, it's only quoted if there's a single argument\, which is useless\, since the MM_Win32 code can only quote singular strings in a command invocation [16​:28] \<@​bulk88> so perl does not escape metacharacters in list mode system() on any OS? [16​:29] \<@​TonyC> bulk88​: it doesn't need to on POSIX systems [16​:29] \<@​bulk88> and doesn't put quotes around yoru list mode args [16​:30] \<@​bulk88> because shell is guarenteeded never to be called in list mode system on nix? [16​:30] \ bulk88​: "If there is only one scalar argument\, the argument is checked for shell metacharacters\, and if there are any\, the entire argument is passed to the system's command shell for parsing" [16​:30] \ other than that there is no mention of meta characters [16​:30] \ so in list mode it doesn't do anything on any os other than call it [16​:31] \ i think the page for system() should have a red \ warning to not use it on windows if you've anything but \w characters [16​:32] \<@​TonyC> it should be fixed\, but tuits [16​:32] \<@​xdg> except on windows\, when list form gets smashed back into a single command line string [16​:32] \<@​xdg> create_command_line in win32.c [16​:32] \ ah\, that's not documented [16​:32] \<@​bulk88> so the problem is on nix char * argv[] is passed unmodified through the OS to the child proc\, on Win32\, char * argv[] is flattened into 1 string by concating\, then split in the child? [16​:33] \ concat​: yes [16​:33] \<@​xdg> bulk88\, IIRC\, CreateProcess hands the command string to the child process and the child process is responsible for parsing [16​:33] \ split​: not sure what you mean\, but after the concat the string is simply sent on by cmd [16​:33] \<@​xdg> But windows migth wash execution through the shell [16​:33] \ xdg​: correct [16​:33] \ and 2 parsing algorithms exist [16​:34] \<@​bulk88> I was refering to the CreateProcess API\, which takes 1 string *\, not argv [16​:36] \ oooh\, that's what you mean with "split in the child" [16​:36] \ yeah\, that's it [16​:36] \ the child process is responsible for making sense of the string [16​:36] \ most will enlist the help of one of 2 parsing functions provided by C libs [16​:37] \<@​bulk88> http​://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft%28v=vs.85%29.aspx http​://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx [16​:37] \<+dipsy> urgh. long url. Try http​://tinyurl.com/8kzrkdf [16​:37] \<+dipsy> [ Parsing C++ Command-Line Arguments (C++) ] [ CommandLineToArgvW function (Windows) ] [16​:37] \ that's one c library [16​:37] \ there's another one with a different function [16​:40] \<@​bulk88> would anyone agree this "windows flattens all cmd line args" behavior breaks perl's nix compatbility in that aspect? since the child process will never recover the correct arg count in the parent process when parsing the string? [16​:40] \<@​bulk88> would auto quoting "fix" this incompatibility with perl on nix? [16​:41] \ bulk88​: in the general case no\, since a child might ignore the quoting [16​:41] \ that question is more easily answered if you specify which child you're talking about [16​:41] \<@​xdg> I wouldn't say "breaks perl's 'nix compatibility". I would say "another 'nix incompatibility on windows" [16​:41] \<@​xdg> since there are already plenty [16​:42] \<@​xdg> It all *should* be better documented in perlport [16​:42] \<@​bulk88> the child is a MS CRT cmd line app [16​:42] \<@​bulk88> such as perl [16​:42] \ in that case proper quoting can fix it [16​:42] \<@​bulk88> not a cygwin proc


-- bulk88 ~ bulk88 at hotmail.com

p5pRT commented 10 years ago

From @Smylers

bulk88 via RT writes​:

[16​:17] \<@​bulk88> "Does exactly the same thing as exec LIST \, except that a fork is done first and the parent process waits for the child process to exit. Note that argument processing varies depending on the number of arguments. If there is more than one argument in LIST\, or if LIST is an array with more than one value\, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument\, the argument is checked for shell metacharacters\, and if there are any\, the entire argument is passed to the system's command shell for parsing (this is /bin/sh -c on Unix platforms\, but varies on other platforms). If there are no shell metacharacters in the argument\, it is split into words and passed directly to execvp \, which is more efficient." [16​:18] \<@​xdg> bulk88\, as I said\, that's wrong. And even perlport doesn't clarify sufficiently [16​:19] \<@​xdg> My opinion is that it's probably a bug\, but no one understands windows shell quoting in Perl well enough to diagnose\, fix or document properly. :-(

If there's consensus on system's current behaviour on Windows being wrong\, or if a plan is developed for what it should be doing\, please can you let this IPC​::System​::Simple ticket know​: https://github.com/pjf/ipc-system-simple/issues/9

IPC​::System​::Simple aims to be compatible with built-in system. That bug report is because sometimes it isn't on Windows — something to do with quoting and arg-splitting.

If built-in system is going to change\, then IPC​::System​::Simple needs to change to match that.

(Also\, it may be worth looking at IPC​::System​::Simple's current behaviour\, just in case that happens to be the desired behaviour for built-in system\, though I suspect not.)

Cheers

Smylers -- http​://twitter.com/Smylers2

p5pRT commented 10 years ago

From @jandubois

Sorry\, I don't have time to look at this in detail right now\, but wanted to point out that there are some extensive tests for the system() quoting behavior on Windows in t/win32/system_tests.

I would recommend to start by extending these tests first to cover the currently broken cases\, to see how it all fits together before designing a new quoting system from scratch.

Cheers\, -Jan

p5pRT commented 9 years ago

From @steve-m-hay

Sorry if this has already been mentioned. I had a skim read of the above and didn't see it\, so I thought I'd record it here for future reference​:

The cmd.exe that unexpectedly gets launched to launch the perl.exe (instead of perl.exe getting launched directly) sometimes doesn't happen when using system("$perl ...") rather than system(1\, "$perl ..."). (The discussion so far has been regarding the latter form\, specifically a case in t/test.pl's watchdog().)

The example I've just stumbled across is that this​:

perl -le "system(1\, qq[$^X -e sleep(10)])"

launches a cmd.exe which launches a perl.exe which sleeps for 10 seconds\, which can be clearly seen in the tree view of Process Explorer​: the cmd.exe->perl.exe pair are separated from the cmd.exe in which I ran the above command because the system(1\, ...) form doesn't wait for the command to complete so the perl.exe than I ran from my cmd.exe immediately exits\, leaving just the cmd.exe->perl.exe that it launched\, now separated from my cmd.exe. You can see this more clearly by running​:

perl -le "system(1\, qq[$^X -e sleep(10)]); sleep(5)"

in which the perl.exe that I ran from my cmd.exe sticks around for 5 seconds with the cmd.exe->perl.exe underneath it before exiting.

Whereas this​:

perl -le "system(qq[$^X -e sleep(10)])"

does NOT launch the unexpected cmd.exe\, which can again be clearly seen in Process Explorer​: the perl.exe than I ran from my cmd.exe only has another perl.exe underneath it.

I don't know if that's a useful observation or not; I just thought I'd mention it since it surprised me.

Another thing I noticed is that shortly after this ticket appeared\, there was another discussion on p5p (see the thread beginning here​: http​://www.nntp.perl.org/group/perl.perl5.porters/2014/04/msg214390.html) which wound up in commit http​://perl5.git.perl.org/perl.git/commit/94d4006a6d\, which noted that\, "On Windows\, only the system PROGRAM LIST syntax will reliably avoid using the shell; system LIST\, even with more than one element\, will fall back to the shell if the first spawn fails."

That suggests to me that changing

perl -le "system(1\, qq[$^X -e sleep(10)])"

to

perl -le "system({$^X} 1\, qq[$^X -e sleep(10)])"

should workaround the problem.

Indeed\, it does\, but seems to reveal some other new problem?! -- the perl.exe that I ran from my cmd.exe does indeed now run the other perl.exe directly\, without an intermediate cmd.exe\, but the second perl.exe (with the -e sleep(10) arguments) hangs indefinitely instead of exiting after 10 seconds.

I wondered if the indirect object part was causing some confusion with the special leading "1"\, but this​:

perl -le "system({$^X} qq[$^X -e sleep(10)])"

(which works around the unexpected cmd.exe problem itself by omitting that leading "1"\, of course\, as noted earlier) also still hangs indefinitely.

In this case\, the system LIST form avoids the unwanted cmd.exe anyway\, i.e. this behaves as expected​:

perl -le "system($^X\, '-e'\, 'sleep(10)')"

and with this multi-element LIST form\, the indirect object syntax no longer hangs indefinitely either\, i.e. this also behaves as expected​:

perl -le "system({$^X} $^X\, '-e'\, 'sleep(10)')"

In fact\, both of these forms also work as expected with the leading "1" too\, i.e.​:

perl -le "system(1\, $^X\, '-e'\, 'sleep(10)')"

perl -le "system({$^X} 1\, $^X\, '-e'\, 'sleep(10)')"

So to summarize all this​:

perl -le "system(1\, qq[$^X -e sleep(10)])" uses cmd.exe

perl -le "system(qq[$^X -e sleep(10)])" ok

perl -le "system({$^X} 1\, qq[$^X -e sleep(10)])" no cmd.exe but hangs

perl -le "system({$^X} qq[$^X -e sleep(10)])" no cmd.exe but hangs

perl -le "system(1\, $^X\, '-e'\, 'sleep(10)')" ok

perl -le "system($^X\, '-e'\, 'sleep(10)')" ok

perl -le "system({$^X} 1\, $^X\, '-e'\, 'sleep(10)')" ok

perl -le "system({$^X} $^X\, '-e'\, 'sleep(10)')" ok

p5pRT commented 9 years ago

From [Unknown Contact. See original ticket]

Sorry if this has already been mentioned. I had a skim read of the above and didn't see it\, so I thought I'd record it here for future reference​:

The cmd.exe that unexpectedly gets launched to launch the perl.exe (instead of perl.exe getting launched directly) sometimes doesn't happen when using system("$perl ...") rather than system(1\, "$perl ..."). (The discussion so far has been regarding the latter form\, specifically a case in t/test.pl's watchdog().)

The example I've just stumbled across is that this​:

perl -le "system(1\, qq[$^X -e sleep(10)])"

launches a cmd.exe which launches a perl.exe which sleeps for 10 seconds\, which can be clearly seen in the tree view of Process Explorer​: the cmd.exe->perl.exe pair are separated from the cmd.exe in which I ran the above command because the system(1\, ...) form doesn't wait for the command to complete so the perl.exe than I ran from my cmd.exe immediately exits\, leaving just the cmd.exe->perl.exe that it launched\, now separated from my cmd.exe. You can see this more clearly by running​:

perl -le "system(1\, qq[$^X -e sleep(10)]); sleep(5)"

in which the perl.exe that I ran from my cmd.exe sticks around for 5 seconds with the cmd.exe->perl.exe underneath it before exiting.

Whereas this​:

perl -le "system(qq[$^X -e sleep(10)])"

does NOT launch the unexpected cmd.exe\, which can again be clearly seen in Process Explorer​: the perl.exe than I ran from my cmd.exe only has another perl.exe underneath it.

I don't know if that's a useful observation or not; I just thought I'd mention it since it surprised me.

Another thing I noticed is that shortly after this ticket appeared\, there was another discussion on p5p (see the thread beginning here​: http​://www.nntp.perl.org/group/perl.perl5.porters/2014/04/msg214390.html) which wound up in commit http​://perl5.git.perl.org/perl.git/commit/94d4006a6d\, which noted that\, "On Windows\, only the system PROGRAM LIST syntax will reliably avoid using the shell; system LIST\, even with more than one element\, will fall back to the shell if the first spawn fails."

That suggests to me that changing

perl -le "system(1\, qq[$^X -e sleep(10)])"

to

perl -le "system({$^X} 1\, qq[$^X -e sleep(10)])"

should workaround the problem.

Indeed\, it does\, but seems to reveal some other new problem?! -- the perl.exe that I ran from my cmd.exe does indeed now run the other perl.exe directly\, without an intermediate cmd.exe\, but the second perl.exe (with the -e sleep(10) arguments) hangs indefinitely instead of exiting after 10 seconds.

I wondered if the indirect object part was causing some confusion with the special leading "1"\, but this​:

perl -le "system({$^X} qq[$^X -e sleep(10)])"

(which works around the unexpected cmd.exe problem itself by omitting that leading "1"\, of course\, as noted earlier) also still hangs indefinitely.

In this case\, the system LIST form avoids the unwanted cmd.exe anyway\, i.e. this behaves as expected​:

perl -le "system($^X\, '-e'\, 'sleep(10)')"

and with this multi-element LIST form\, the indirect object syntax no longer hangs indefinitely either\, i.e. this also behaves as expected​:

perl -le "system({$^X} $^X\, '-e'\, 'sleep(10)')"

In fact\, both of these forms also work as expected with the leading "1" too\, i.e.​:

perl -le "system(1\, $^X\, '-e'\, 'sleep(10)')"

perl -le "system({$^X} 1\, $^X\, '-e'\, 'sleep(10)')"

So to summarize all this​:

perl -le "system(1\, qq[$^X -e sleep(10)])" uses cmd.exe

perl -le "system(qq[$^X -e sleep(10)])" ok

perl -le "system({$^X} 1\, qq[$^X -e sleep(10)])" no cmd.exe but hangs

perl -le "system({$^X} qq[$^X -e sleep(10)])" no cmd.exe but hangs

perl -le "system(1\, $^X\, '-e'\, 'sleep(10)')" ok

perl -le "system($^X\, '-e'\, 'sleep(10)')" ok

perl -le "system({$^X} 1\, $^X\, '-e'\, 'sleep(10)')" ok

perl -le "system({$^X} $^X\, '-e'\, 'sleep(10)')" ok

toddr commented 4 years ago

@steve-m-hay @bulk88 this case has been idle for 5 years. I assume nothing has changed on this problem. Can you suggest what we might want to do to move this forward?