make-w32
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: WINDOWS32 "Signal 127" due to a path in quotes bug [Ooops]


From: Alessandro Vesely
Subject: Re: WINDOWS32 "Signal 127" due to a path in quotes bug [Ooops]
Date: Mon, 08 Aug 2005 09:32:52 +0200
User-agent: Mozilla Thunderbird 1.0.6 (Windows/20050716)

Eli Zaretskii wrote:
Date: Sun, 07 Aug 2005 15:49:57 +0200
From: Alessandro Vesely <address@hidden>

I tested w98, nt4, and win2000 and found no way (except TerminateProcess)
to interrupt batch files running under command/cmd: GenerateConsoleCtrlEvent
reaches its currently active executable child which can catch it. But then
command/cmd carries on running the next batch line.

Doesn't cmd ask whether to terminate the batch job?

Only w2k does so. It asks and waits for an answer. If I manually give
any answer it executes the next line of the batch. Otherwise it just
stays there (see example below.)


Anyway, if you could send a sample Makefile, the related batch files
and programs, and a transcript of the session that could be used to
reproduce this problem, I'll try to look at that when I have time.

I tested with a simple program that just calls CreateProcess
optionally setting error mode and installing a ctrl-handler:
that makes testing easier, so I send you that program.

After CreateProcess, it starts sending Ctrl-C/Break events to the
child after 10 secs and halves that time while the child survives.
The ctrl-c handler ignores ctrl events only if a child is running.
After a couple of Ctrl-Cs and a Ctrl-Break, em calls TerminateProcess.
On w98 that call does not terminate the batch either.

On w2k, em runs like so:

----- command box start -----
Microsoft Windows 2000 [Versione 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.

C:\>cd ale\tmp

C:\ale\tmp>em -h
Error Mode does not alter Ctrl-C behaviour - syntax:
   em (-flags | command [argument])...
where flags is one or more of the following
a    one argument follows commad
C    use Comspec to run command
c    run command as an executable
E    run argument with error mode flag
e    run argument without error mode flag
h    this summary
M    set error mode SEM_FAILCRITICALERRORS
m    set error mode to default
S    set the Ctrl-C handler
s    unset the previously set Ctrl-C handler
wNN  wait NN milliseconds

This version of em was compiled with
DEFAULT_TIMEOUT = 10000
STDERR  = (&_streams[2])

C:\ale\tmp>cat foo.bat
em -Sw20000
em -Sw20000
em -Sw20000
em -Sw20000

C:\ale\tmp>em -SC foo.bat
em 1416: running ""C:\WINNT\system32\cmd.exe" /C foo.bat" with errormode

C:\ale\tmp>em -Sw20000
em 1292: waiting 20000ms

em 1416: Ctrl-C for foo.bat not terminated after 10000 ms; (t -> 5000)
em 1292: caught Ctrl-C
em 1416: ignoring Ctrl-C
^CTerminare il processo batch (S/N)?
em 1416: Ctrl-C for foo.bat not terminated after 15000 ms; (t -> 2500)
^Cem 1416: ignoring Ctrl-C

em 1416: Ctrl-Break for foo.bat not terminated after 17500 ms; (t -> 1250)
^Cem 1416: ignoring Ctrl-Break

em 1416: ABORT for foo.bat not terminated after 18750 ms; (t -> 625)
em 1416: Rtc=259 from "C:\WINNT\system32\cmd.exe" /C foo.bat

----- command box end -----
Guess `^C's are written by cmd.exe's ctrl-handler.
Dunno what 259 (ERROR_NO_MORE_ITEMS) means
/*
* em.c - experimenting with error mode
*/
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <windows.h>

#if !defined(STDERR)
#define STDERR stderr
#endif

#if !defined(DEFAULT_TIMEOUT)
#define DEFAULT_TIMEOUT 10000
#endif

#define get_quoted(x) #x
#define stringer(x) get_quoted(x)
char const def_stderr[] = stringer(STDERR);
char const def_timeout[] = stringer(DEFAULT_TIMEOUT);

#if !defined(PATH_MAX)
#if defined(MAX_PATH)
#define PATH_MAX MAX_PATH
#else
#define PATH_MAX 2048
#endif
#endif


static int running_command;
static int exit_prog;



BOOL WINAPI ctrl_handler(DWORD signal)
{
        /*
        * If command is running, do nothing on ctrl-c/break
        */
        if (signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT)
        {
                char *event = signal == CTRL_C_EVENT ? "C" : "Break";
                if (running_command)
                {
                        fprintf(STDERR, "em %u: ignoring Ctrl-%s\n",
                                GetCurrentProcessId(), event);
                        ++exit_prog;
                        return TRUE;
                }
                fprintf(STDERR, "em %u: caught Ctrl-%s\n",
                        GetCurrentProcessId(), event);

                /* don't handle it, let default handler run */
        }

        return FALSE;
}


static int run_command(char *cmd, char *arg, int error_mode, int want_comspec)
{
        char app[PATH_MAX];
        DWORD rtc;
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
        DWORD timeout = DEFAULT_TIMEOUT, elapsed = 0;
        DWORD const process_creation_flags = error_mode ?
                CREATE_DEFAULT_ERROR_MODE : 0;

        int generated_ctrl = 0;

        // lpCommandLine
        // [in, out] Pointer to a null-terminated string that specifies
        // the command line to execute. The maximum length of this string
        // is 32K characters.
        char *cmdline;

        if ((cmdline = (char*) malloc(32*1024)) == NULL)
        {
                return -1;
        }

        rtc = 0;

        if (want_comspec)
        {
                if (GetEnvironmentVariable("ComSpec", app, sizeof app))
                {
                        sprintf(cmdline, "\"%s\" /C %s", app, cmd);
                }
                else
                {
                        rtc = -1;
                        fprintf(STDERR, "em %u: can't read ComSpec for %s 
(err=%d)\n",
                                GetCurrentProcessId(),
                                cmd, GetLastError());
                }
        }
        else
        {
                strcpy(app, cmd);
                strcpy(cmdline, cmd);
        }

        if (arg != NULL)
        {
                strcat(cmdline, " ");
                strcat(cmdline, arg);
        }

        memset(&pi, 0, sizeof pi);
        memset(&si, 0, sizeof si);
        si.cb = sizeof si;
        si.dwFlags = STARTF_USESTDHANDLES;
        si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
        si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
        si.hStdError = GetStdHandle(STD_ERROR_HANDLE);


        fprintf(STDERR, "em %u: running \"%s\" with%s errormode\n",
                GetCurrentProcessId(),
                cmdline, error_mode ? "" : "out");

        /*
        * Create the process in the current console. This is practical
        * but inhibits killing the app, because only one process may
        * run on a VM/Console at a time, hence we get back with WAIT_TIMEOUT
        * after the process has actually timed out and stopped
        */
        running_command = 1;
        if (rtc == 0 && CreateProcess(
                app,                          // application name
                cmdline,                      // 32K command line
                NULL,                         // process security attributes
                NULL,                         // thread security attributes
                TRUE,                         // inherit handles
                process_creation_flags,       // process creation flags
                NULL,                         // environment
                NULL,                         // working directory
                &si,                          // startup info
                &pi))                         // result handles
        {
                static const struct event_type
                {
                        DWORD ctrl; char const*descr;
                } to_action[] =
                {
                        {CTRL_C_EVENT, "Ctrl-C"},
                        {CTRL_C_EVENT, "Ctrl-C"},
                        {CTRL_BREAK_EVENT, "Ctrl-Break"},
                        {0, "ABORT"} // last_to
                };

                DWORD exit_code = 0;
                DWORD wrtc;
                int to = 0;

                while (to < sizeof to_action / sizeof to_action[0])
                {
                        int const last_to = to + 1 >= sizeof to_action / sizeof 
to_action[0];
                        wrtc = WaitForSingleObject(pi.hProcess, timeout ? 
timeout : INFINITE);
                        if (!GetExitCodeProcess(pi.hProcess, &exit_code))
                                exit_code = last_to ? ERROR_OPERATION_ABORTED : 
0;
                        if (wrtc != WAIT_TIMEOUT || exit_code && exit_code != 
STILL_ACTIVE)
                                break;

                        elapsed += timeout;
                        if (timeout > 400)
                                timeout /= 2;

                        fprintf(STDERR, "\nem %u: "
                                "%s for %s not terminated after %d ms; (t -> 
%d)\n",
                                        GetCurrentProcessId(), 
to_action[to].descr,
                                        cmd, elapsed, timeout);

                        if (last_to)
                                TerminateProcess(pi.hProcess, 
ERROR_OPERATION_ABORTED);
                        else
                        {
                                GenerateConsoleCtrlEvent(to_action[to].ctrl, 0);
                                ++generated_ctrl;
#if 0 // doesnt't work
                                if (want_comspec)
                                {
                                        
GenerateConsoleCtrlEvent(to_action[to].ctrl, 0);
                                        ++generated_ctrl;
                                }
#endif
                        }

                        ++to;
                }

                fprintf(stdout, "em %u: " "Rtc=%d from %s\n",
                        GetCurrentProcessId(),
                        exit_code, cmdline);

                CloseHandle(pi.hThread);
                CloseHandle(pi.hProcess);
                rtc = exit_code != 0;
        }
        else if (rtc == 0)
        {
                running_command = 0;
                rtc = -1;
                fprintf(STDERR, "em %u: error %d: Cannot execute %s\n",
                        GetCurrentProcessId(),
                        GetLastError(), cmdline);
        }

        running_command = 0;
        exit_prog -= generated_ctrl;

        /* still ctrl-break? */
        if (exit_prog)
        {
                fprintf(STDERR, "em %u: exit was requested while running %s\n",
                        GetCurrentProcessId(),
                                cmd, GetLastError());
                return -1;
        }


        free(cmdline);
        return rtc;
}

char const help[] =
"Error Mode does not alter Ctrl-C behaviour - syntax:\n"
"   em (-flags | command [argument])...\n"
"where flags is one or more of the following\n"
"a    one argument follows commad\n"
"C    use Comspec to run command\n"
"c    run command as an executable\n"
"E    run argument with error mode flag\n"
"e    run argument without error mode flag\n"
"h    this summary\n"
"M    set error mode SEM_FAILCRITICALERRORS\n"
"m    set error mode to default\n"
"S    set the Ctrl-C handler\n"
"s    unset the previously set Ctrl-C handler\n"
"wNN  wait NN milliseconds\n"
"\n"
"This version of em was compiled with\n"
"DEFAULT_TIMEOUT = %s\n"
"STDERR  = %s\n";

int main(int argc, char *argv[])
{
        int i, errs = 0;
        int error_mode = 1, want_comspec = 0;
        int with_arg = 0;

        for (i = 1; i < argc; ++i)
        {
                char *arg = argv[i];
                if (arg[0] == '-' && arg[1] != 0)
                {
                        int ch;
                        for (ch = *++arg; ch; ch = *++arg)
                                switch (ch)
                                {
                                        case 'a':
                                                with_arg = 1;
                                                break;

                                        case 'c':
                                                want_comspec = 0;
                                                break;

                                        case 'C':
                                                want_comspec = 1;
                                                break;

                                        case 'e':
                                                error_mode = 0;
                                                break;

                                        case 'E':
                                                error_mode = 1;
                                                break;

                                        case 'h':
                                                fprintf(STDERR, help, 
def_timeout, def_stderr);
                                                break;

                                        case 'M':
                                                
SetErrorMode(SEM_FAILCRITICALERRORS);
                                                break;

                                        case 'm':
                                                SetErrorMode(0);
                                                break;

                                        case 'S':
                                                
SetConsoleCtrlHandler(&ctrl_handler, TRUE);
                                                break;

                                        case 's':
                                                
SetConsoleCtrlHandler(&ctrl_handler, FALSE);
                                                break;

                                        case 'w':
                                        {
                                                char *t;
                                                int wait = strtoul(arg + 1, &t, 
0);
                                                if (wait > 0)
                                                {
                                                        fprintf(STDERR, "em %u: 
waiting %dms\n",
                                                                
GetCurrentProcessId(), wait);
                                                        Sleep(wait);
                                                        fprintf(STDERR, "em %u: 
done waiting %dms\n",
                                                                
GetCurrentProcessId(), wait);
                                                        arg = t - 1;
                                                }
                                                break;
                                        }

                                        default:
                                                fprintf(STDERR, "em %u: invalid 
option %c in %s\n",
                                                        GetCurrentProcessId(),
                                                        ch, argv[i]);
                                                ++errs;
                                                break;
                                }
                }
                else
                {
                        char *aarg = with_arg ? argv[++i] : NULL;
                        int rtc = run_command(arg, aarg, error_mode, 
want_comspec);
                        if (rtc < 0)
                        {
                                errs = 1;
                                break;
                        }

                        errs += rtc;
                        with_arg = 0;
                }
        }

        return errs != 0;
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]