From c5ca1bc929a933daefabab9f1be18c5653c7927d Mon Sep 17 00:00:00 2001 From: usa Date: Tue, 13 Nov 2001 03:59:20 +0000 Subject: [PATCH] * hash.c (envix): use GET_ENVIRON and FREE_ENVIRON to get environment variables list. * hash.c (env_keys): ditto. * hash.c (env_each_key): ditto. * hash.c (env_values): ditto. * hash.c (env_keys): ditto. * hash.c (env_each_value): ditto. * hash.c (env_each): ditto. * hash.c (env_inspect): ditto. * hash.c (env_to_a): ditto. * hash.c (env_size): ditto. * hash.c (env_empty_p): ditto. * hash.c (env_has_value): ditto. * hash.c (env_index): ditto. * hash.c (env_to_hash): ditto. * win32/win32.c (win32_getenv): use static buffer. * win32/win32.c, win32/win32.h (win32_get_environ): get environment variables list. [new] * win32/win32.c, win32/win32.h (win32_free_environ): free environment variables list. [new] * win32/win32.c (do_spawn): use CreateChild() instead of calling CreateProcess() directly. Original patches comes from Patrick Cheng. * win32/win32.c (mypopen): ditto. * win32/win32.c (mypclose): use rb_syswait() instead of waiting in this function. * win32/win32.c (waitpid): use wait_child() instead of _cwait(). * win32/win32.c (CreateChild): added. [new] * win32/win32.c (wait_child): added. [new] * win32/win32.c (FindFirstChildSlot): added. [new] * win32/win32.c (FindChildSlot): added. [new] * win32/win32.c (FindPipedChildSlot): added. [new] * win32/win32.c (CloseChildHandle): added. [new] * win32/win32.c (FindFreeChildSlot): added. [new] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1823 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 65 +++++ hash.c | 78 +++-- win32/win32.c | 774 ++++++++++++++++++++++---------------------------- win32/win32.h | 2 + 4 files changed, 469 insertions(+), 450 deletions(-) diff --git a/ChangeLog b/ChangeLog index 43ec1ab2a3..e2415320ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,68 @@ +Tue Nov 13 12:55:59 2001 Usaku Nakamura + + * win32/win32.c (do_spawn): use CreateChild() instead of calling + CreateProcess() directly. Original patches comes from Patrick Cheng. + + * win32/win32.c (mypopen): ditto. + + * win32/win32.c (mypclose): use rb_syswait() instead of waiting in this + function. + + * win32/win32.c (waitpid): use wait_child() instead of _cwait(). + + * win32/win32.c (CreateChild): added. [new] + + * win32/win32.c (wait_child): added. [new] + + * win32/win32.c (FindFirstChildSlot): added. [new] + + * win32/win32.c (FindChildSlot): added. [new] + + * win32/win32.c (FindPipedChildSlot): added. [new] + + * win32/win32.c (CloseChildHandle): added. [new] + + * win32/win32.c (FindFreeChildSlot): added. [new] + +Tue Nov 13 12:38:12 2001 Usaku Nakamura + + * hash.c (envix): use GET_ENVIRON and FREE_ENVIRON to get environment + variables list. + + * hash.c (env_keys): ditto. + + * hash.c (env_each_key): ditto. + + * hash.c (env_values): ditto. + + * hash.c (env_keys): ditto. + + * hash.c (env_each_value): ditto. + + * hash.c (env_each): ditto. + + * hash.c (env_inspect): ditto. + + * hash.c (env_to_a): ditto. + + * hash.c (env_size): ditto. + + * hash.c (env_empty_p): ditto. + + * hash.c (env_has_value): ditto. + + * hash.c (env_index): ditto. + + * hash.c (env_to_hash): ditto. + + * win32/win32.c (win32_getenv): use static buffer. + + * win32/win32.c, win32/win32.h (win32_get_environ): get environment + variables list. [new] + + * win32/win32.c, win32/win32.h (win32_free_environ): free environment + variables list. [new] + Fri Nov 9 13:50:06 2001 Usaku Nakamura * win32/config.status.in: make CFLAGS same as Makefile's one. diff --git a/hash.c b/hash.c index 7eb519b8b2..3ca5f88927 100644 --- a/hash.c +++ b/hash.c @@ -843,10 +843,18 @@ rb_hash_update(hash1, hash2) static int path_tainted = -1; -#ifndef NT -extern char **environ; -#endif static char **origenviron; +#ifdef NT +#define GET_ENVIRON(e) (e = win32_get_environ()) +#define FREE_ENVIRON(e) win32_free_environ(e) +static char **my_environ; +#undef environ +#define environ my_environ +#else +extern char **environ; +#define GET_ENVIRON(e) (e) +#define FREE_ENVIRON(e) +#endif static VALUE env_delete(obj, name) @@ -955,17 +963,20 @@ envix(nam) char *nam; { register int i, len = strlen(nam); + char **env; - for (i = 0; environ[i]; i++) { + env = GET_ENVIRON(environ); + for (i = 0; env[i]; i++) { if ( #ifdef WIN32 - strnicmp(environ[i],nam,len) == 0 + strnicmp(env[i],nam,len) == 0 #else - memcmp(environ[i],nam,len) == 0 + memcmp(env[i],nam,len) == 0 #endif - && environ[i][len] == '=') + && env[i][len] == '=') break; /* memcmp must come first to avoid */ } /* potential SEGV's */ + FREE_ENVIRON(environ); return i; } @@ -1141,7 +1152,7 @@ env_keys() char **env; VALUE ary = rb_ary_new(); - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1149,6 +1160,7 @@ env_keys() } env++; } + FREE_ENVIRON(environ); return ary; } @@ -1158,7 +1170,7 @@ env_each_key(hash) { char **env; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1166,6 +1178,7 @@ env_each_key(hash) } env++; } + FREE_ENVIRON(environ); return Qnil; } @@ -1175,7 +1188,7 @@ env_values() char **env; VALUE ary = rb_ary_new(); - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1183,6 +1196,7 @@ env_values() } env++; } + FREE_ENVIRON(environ); return ary; } @@ -1192,7 +1206,7 @@ env_each_value(hash) { char **env; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1200,6 +1214,7 @@ env_each_value(hash) } env++; } + FREE_ENVIRON(environ); return Qnil; } @@ -1209,7 +1224,7 @@ env_each(hash) { char **env; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1218,6 +1233,7 @@ env_each(hash) } env++; } + FREE_ENVIRON(environ); return Qnil; } @@ -1267,7 +1283,7 @@ env_inspect() VALUE str = rb_str_buf_new2("{"); VALUE i; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); @@ -1283,6 +1299,7 @@ env_inspect() } env++; } + FREE_ENVIRON(environ); rb_str_buf_cat2(str, "}"); OBJ_TAINT(str); @@ -1295,7 +1312,7 @@ env_to_a() char **env; VALUE ary = rb_ary_new(); - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1304,6 +1321,7 @@ env_to_a() } env++; } + FREE_ENVIRON(environ); return ary; } @@ -1317,16 +1335,26 @@ static VALUE env_size() { int i; + char **env; - for(i=0; environ[i]; i++) + env = GET_ENVIRON(environ); + for(i=0; env[i]; i++) ; + FREE_ENVIRON(environ); return INT2FIX(i); } static VALUE env_empty_p() { - if (environ[0] == 0) return Qtrue; + char **env; + + env = GET_ENVIRON(environ); + if (env[0] == 0) { + FREE_ENVIRON(environ); + return Qtrue; + } + FREE_ENVIRON(environ); return Qfalse; } @@ -1350,15 +1378,18 @@ env_has_value(dmy, value) char **env; if (TYPE(value) != T_STRING) return Qfalse; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '=')+1; if (s) { - if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) + if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) { + FREE_ENVIRON(environ); return Qtrue; + } } env++; } + FREE_ENVIRON(environ); return Qfalse; } @@ -1367,18 +1398,22 @@ env_index(dmy, value) VALUE dmy, value; { char **env; + VALUE str; if (TYPE(value) != T_STRING) return Qnil; - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '=')+1; if (s) { if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) { - return rb_tainted_str_new(*env, s-*env-1); + str = rb_tainted_str_new(*env, s-*env-1); + FREE_ENVIRON(environ); + return str; } } env++; } + FREE_ENVIRON(environ); return Qnil; } @@ -1413,7 +1448,7 @@ env_to_hash() char **env; VALUE hash = rb_hash_new(); - env = environ; + env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { @@ -1422,6 +1457,7 @@ env_to_hash() } env++; } + FREE_ENVIRON(environ); return hash; } diff --git a/win32/win32.c b/win32/win32.c index 53c7c22467..99a703c6b5 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -72,6 +72,7 @@ extern char **environ; #define environ _environ #endif +static struct ChildRecord *CreateChild(char *, SECURITY_ATTRIBUTES *, HANDLE, HANDLE, HANDLE); static bool NtHasRedirection (char *); static int valid_filename(char *s); static void StartSockets (); @@ -294,24 +295,74 @@ char *getlogin() #if 1 -// popen stuff +#define MAXCHILDNUM 256 /* max num of child processes */ -// -// use these so I can remember which index is which -// +struct ChildRecord { + HANDLE hProcess; /* process handle */ + pid_t pid; /* process id */ + FILE* pipe; /* pipe */ +} ChildRecord[MAXCHILDNUM]; -#define NtPipeRead 0 // index of pipe read descriptor -#define NtPipeWrite 1 // index of pipe write descriptor +#define FOREACH_CHILD(v) do { \ + struct ChildRecord* v; \ + for (v = ChildRecord; v < ChildRecord + sizeof(ChildRecord) / sizeof(ChildRecord[0]); ++v) +#define END_FOREACH_CHILD } while (0) -#define NtPipeSize 1024 // size of pipe buffer +static struct ChildRecord * +FindFirstChildSlot(void) +{ + FOREACH_CHILD(child) { + if (child->pid) return child; + } END_FOREACH_CHILD; + return NULL; +} -#define MYPOPENSIZE 256 // size of book keeping structure +static struct ChildRecord * +FindChildSlot(pid_t pid) +{ + + FOREACH_CHILD(child) { + if (child->pid == pid) { + return child; + } + } END_FOREACH_CHILD; + return NULL; +} + +static struct ChildRecord * +FindPipedChildSlot(FILE *fp) +{ + FOREACH_CHILD(child) { + if (child->pid && child->pipe == fp) { + return child; + } + } END_FOREACH_CHILD; + return NULL; +} + +static void +CloseChildHandle(struct ChildRecord *child) +{ + HANDLE h = child->hProcess; + child->hProcess = NULL; + child->pid = 0; + CloseHandle(h); +} + +static struct ChildRecord * +FindFreeChildSlot(void) +{ + FOREACH_CHILD(child) { + if (!child->pid) { + child->pid = -1; /* lock the slot */ + child->hProcess = NULL; + child->pipe = NULL; + return child; + } + } END_FOREACH_CHILD; + return NULL; +} -struct { - int inuse; - int pid; - FILE *pipe; -} MyPopenRecord[MYPOPENSIZE]; int SafeFree(char **vec, int vecc) { @@ -398,34 +449,13 @@ FILE * mypopen (char *cmd, char *mode) { FILE *fp; - int saved, reading; + int reading; int pipemode; - int pipes[2]; - int pid; - int slot; - static initialized = 0; - - // - // if first time through, intialize our book keeping structure - // - - if (!initialized++) { - for (slot = 0; slot < MYPOPENSIZE; slot++) - MyPopenRecord[slot].inuse = FALSE; - } - - //printf("mypopen %s\n", cmd); - - // - // find a free popen slot - // - - for (slot = 0; slot < MYPOPENSIZE && MyPopenRecord[slot].inuse; slot++) - ; - - if (slot > MYPOPENSIZE) { - return NULL; - } + struct ChildRecord* child; + BOOL fRet; + HANDLE hInFile, hOutFile; + SECURITY_ATTRIBUTES sa; + int fd; // // Figure out what we\'re doing... @@ -437,410 +467,183 @@ mypopen (char *cmd, char *mode) // // Now get a pipe // + sa.nLength = sizeof (SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; -#if 0 - if (_pipe(pipes, NtPipeSize, pipemode) == -1) { - return NULL; + fRet = CreatePipe(&hInFile, &hOutFile, &sa, 2048L); + if (!fRet) { + errno = GetLastError(); + rb_sys_fail("mypopen: CreatePipe"); } if (reading) { - - // - // we\'re reading from the pipe, so we must hook up the - // write end of the pipe to the new processes stdout. - // To do this we must save our file handle from stdout - // by _dup\'ing it, then setting our stdout to be the pipe\'s - // write descriptor. We must also make the write handle - // inheritable so the new process can use it. - - if ((saved = _dup(fileno(stdout))) == -1) { - _close(pipes[NtPipeRead]); - _close(pipes[NtPipeWrite]); - return NULL; - } - if (_dup2 (pipes[NtPipeWrite], fileno(stdout)) == -1) { - _close(pipes[NtPipeRead]); - _close(pipes[NtPipeWrite]); - return NULL; - } + child = CreateChild(cmd, &sa, NULL, hOutFile, NULL); } else { - // - // must be writing to the new process. Do the opposite of - // the above, i.e. hook up the processes stdin to the read - // end of the pipe. - // - - if ((saved = _dup(fileno(stdin))) == -1) { - _close(pipes[NtPipeRead]); - _close(pipes[NtPipeWrite]); - return NULL; - } - if (_dup2(pipes[NtPipeRead], fileno(stdin)) == -1) { - _close(pipes[NtPipeRead]); - _close(pipes[NtPipeWrite]); - return NULL; - } + child = CreateChild(cmd, &sa, hInFile, NULL, NULL); } - // - // Start the new process. Must set _fileinfo to non-zero value - // for file descriptors to be inherited. Reset after the process - // is started. - // - - if (NtHasRedirection(cmd)) { - docmd: - pid = spawnlpe(_P_NOWAIT, "cmd.exe", "/c", cmd, 0, environ); - if (pid == -1) { - _close(pipes[NtPipeRead]); - _close(pipes[NtPipeWrite]); - return NULL; - } - } - else { - char **vec; - int vecc = NtMakeCmdVector(cmd, &vec, FALSE); - - //pid = spawnvpe (_P_NOWAIT, vec[0], vec, environ); - pid = spawnvpe (_P_WAIT, vec[0], vec, environ); - if (pid == -1) { - goto docmd; - } - Safefree (vec, vecc); + if (!child) { + CloseHandle(hInFile); + CloseHandle(hOutFile); + rb_sys_fail("mypopen: CreateChild"); } if (reading) { - - // - // We need to close our instance of the inherited pipe write - // handle now that it's been inherited so that it will actually close - // when the child process ends. - // - - if (_close(pipes[NtPipeWrite]) == -1) { - _close(pipes[NtPipeRead]); - return NULL; - } - if (_dup2 (saved, fileno(stdout)) == -1) { - _close(pipes[NtPipeRead]); - return NULL; - } - _close(saved); - - // - // Now get a stream pointer to return to the calling program. - // - - if ((fp = (FILE *) fdopen(pipes[NtPipeRead], mode)) == NULL) { - return NULL; - } + fd = _open_osfhandle((long)hInFile, (_O_RDONLY | pipemode)); + CloseHandle(hOutFile); } else { - - // - // need to close our read end of the pipe so that it will go - // away when the write end is closed. - // - - if (_close(pipes[NtPipeRead]) == -1) { - _close(pipes[NtPipeWrite]); - return NULL; - } - if (_dup2 (saved, fileno(stdin)) == -1) { - _close(pipes[NtPipeWrite]); - return NULL; - } - _close(saved); - - // - // Now get a stream pointer to return to the calling program. - // - - if ((fp = (FILE *) fdopen(pipes[NtPipeWrite], mode)) == NULL) { - _close(pipes[NtPipeWrite]); - return NULL; - } + fd = _open_osfhandle((long)hOutFile, (_O_WRONLY | pipemode)); + CloseHandle(hInFile); } - // - // do the book keeping - // + if (fd == -1) { + CloseHandle(reading ? hInFile : hOutFile); + CloseChildHandle(child); + rb_sys_fail("mypopen: _open_osfhandle"); + } - MyPopenRecord[slot].inuse = TRUE; - MyPopenRecord[slot].pipe = fp; - MyPopenRecord[slot].pid = pid; + if ((fp = (FILE *) fdopen(fd, mode)) == NULL) { + _close(fd); + CloseChildHandle(child); + rb_sys_fail("mypopen: fdopen"); + } + + child->pipe = fp; return fp; -#else - { - int p[2]; - - BOOL fRet; - HANDLE hInFile, hOutFile; - LPCSTR lpApplicationName = NULL; - LPTSTR lpCommandLine; - LPTSTR lpCmd2 = NULL; - DWORD dwCreationFlags; - STARTUPINFO aStartupInfo; - PROCESS_INFORMATION aProcessInformation; - SECURITY_ATTRIBUTES sa; - int fd; - - sa.nLength = sizeof (SECURITY_ATTRIBUTES); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - fRet = CreatePipe(&hInFile, &hOutFile, &sa, 2048L); - if (!fRet) { - errno = GetLastError(); - rb_sys_fail("mypopen: CreatePipe"); - } - - memset(&aStartupInfo, 0, sizeof (STARTUPINFO)); - memset(&aProcessInformation, 0, sizeof (PROCESS_INFORMATION)); - aStartupInfo.cb = sizeof (STARTUPINFO); - aStartupInfo.dwFlags = STARTF_USESTDHANDLES; - - if (reading) { - aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - aStartupInfo.hStdOutput = hOutFile; - } - else { - aStartupInfo.hStdInput = hInFile; - aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - } - aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - dwCreationFlags = (NORMAL_PRIORITY_CLASS); - - lpCommandLine = cmd; - if (NtHasRedirection(cmd) || isInternalCmd(cmd)) { - lpApplicationName = getenv("COMSPEC"); - lpCmd2 = xmalloc(strlen(lpApplicationName) + 1 + strlen(cmd) + sizeof (" /c ")); - sprintf(lpCmd2, "%s %s%s", lpApplicationName, " /c ", cmd); - lpCommandLine = lpCmd2; - } - - fRet = CreateProcess(lpApplicationName, lpCommandLine, &sa, &sa, - sa.bInheritHandle, dwCreationFlags, NULL, NULL, &aStartupInfo, &aProcessInformation); - errno = GetLastError(); - - if (lpCmd2) - free(lpCmd2); - - if (!fRet) { - CloseHandle(hInFile); - CloseHandle(hOutFile); - return NULL; - } - - CloseHandle(aProcessInformation.hThread); - - if (reading) { - fd = _open_osfhandle((long)hInFile, (_O_RDONLY | pipemode)); - CloseHandle(hOutFile); - } - else { - fd = _open_osfhandle((long)hOutFile, (_O_WRONLY | pipemode)); - CloseHandle(hInFile); - } - - if (fd == -1) { - CloseHandle(reading ? hInFile : hOutFile); - CloseHandle(aProcessInformation.hProcess); - rb_sys_fail("mypopen: _open_osfhandle"); - } - - if ((fp = (FILE *) fdopen(fd, mode)) == NULL) { - _close(fd); - CloseHandle(aProcessInformation.hProcess); - rb_sys_fail("mypopen: fdopen"); - } - - MyPopenRecord[slot].inuse = TRUE; - MyPopenRecord[slot].pipe = fp; - MyPopenRecord[slot].pid = (int)aProcessInformation.hProcess; - return fp; - } -#endif } +extern VALUE rb_last_status; + int mypclose(FILE *fp) { - int i; - DWORD exitcode; + struct ChildRecord *child = FindPipedChildSlot(fp); - Sleep(100); - for (i = 0; i < MYPOPENSIZE; i++) { - if (MyPopenRecord[i].inuse && MyPopenRecord[i].pipe == fp) - break; + if (!child) { + return -1; /* may closed in waitpid() */ } - if (i >= MYPOPENSIZE) { - rb_fatal("Invalid file pointer passed to mypclose!\n"); - } - - // - // get the return status of the process - // - -#if 0 - if (_cwait(&exitcode, MyPopenRecord[i].pid, WAIT_CHILD) == -1) { - if (errno == ECHILD) { - fprintf(stderr, "mypclose: nosuch child as pid %x\n", - MyPopenRecord[i].pid); - } - } -#else - for (;;) { - if (GetExitCodeProcess((HANDLE)MyPopenRecord[i].pid, &exitcode)) { - if (exitcode == STILL_ACTIVE) { - //printf("Process is Active.\n"); - Sleep(100); - TerminateProcess((HANDLE)MyPopenRecord[i].pid, 0); // ugly... - continue; - } - else if (exitcode == 0) { - //printf("done.\n"); - break; - } - else { - //printf("never.\n"); - break; - } - } - } - CloseHandle((HANDLE)MyPopenRecord[i].pid); -#endif // // close the pipe // - + child->pipe = NULL; fflush(fp); fclose(fp); // - // free this slot + // get the return status of the process // - - MyPopenRecord[i].inuse = FALSE; - MyPopenRecord[i].pipe = NULL; - MyPopenRecord[i].pid = 0; - - return (int)((exitcode & 0xff) << 8); + rb_syswait(child->pid); + return NUM2INT(rb_last_status); } #endif #if 1 -typedef char* CHARP; -/* - * The following code is based on the do_exec and do_aexec functions - * in file doio.c - */ - int do_spawn(cmd) char *cmd; { - register char **a; - register char *s; - char **argv; - int status = -1; - char *shell, *cmd2; - int mode = NtSyncProcess ? P_WAIT : P_NOWAIT; - char quote; - char *exec; - - /* save an extra exec if possible */ - if ((shell = getenv("RUBYSHELL")) != 0) { - if (NtHasRedirection(cmd)) { - int i; - char *p; - char *argv[4]; - char *cmdline = ALLOC_N(char, (strlen(cmd) * 2 + 1)); - - p=cmdline; - *p++ = '"'; - for (s=cmd; *s;) { - if (*s == '"') - *p++ = '\\'; /* Escape d-quote */ - *p++ = *s++; - } - *p++ = '"'; - *p = '\0'; - - /* fprintf(stderr, "do_spawn: %s %s\n", shell, cmdline); */ - argv[0] = shell; - argv[1] = "-c"; - argv[2] = cmdline; - argv[4] = NULL; - status = spawnvpe(mode, argv[0], argv, environ); - /* return spawnle(mode, shell, shell, "-c", cmd, (char*)0, environ); */ - free(cmdline); - return (int)((status & 0xff) << 8); - } + struct ChildRecord *child = CreateChild(cmd, NULL, NULL, NULL, NULL); + if (!child) { + rb_sys_fail("do_spawn: CreateChild"); } - else if ((shell = getenv("COMSPEC")) != 0) { - if (NtHasRedirection(cmd) /* || isInternalCmd(cmd) */) { - status = spawnle(mode, shell, shell, "/c", cmd, (char*)0, environ); - return (int)((status & 0xff) << 8); - } + rb_syswait(child->pid); + return NUM2INT(rb_last_status); +} + +static struct ChildRecord * +CreateChild(char *cmd, SECURITY_ATTRIBUTES *psa, HANDLE hInput, HANDLE hOutput, HANDLE hError) +{ + BOOL fRet; + DWORD dwCreationFlags; + STARTUPINFO aStartupInfo; + PROCESS_INFORMATION aProcessInformation; + SECURITY_ATTRIBUTES sa; + char *shell; + struct ChildRecord *child; + + child = FindFreeChildSlot(); + if (!child) { + errno = EAGAIN; + return NULL; } - argv = ALLOC_N(CHARP, (strlen(cmd) / 2 + 2)); - cmd2 = ALLOC_N(char, (strlen(cmd) + 1)); - strcpy(cmd2, cmd); - a = argv; - for (s = cmd2; *s;) { - while (*s && ISSPACE(*s)) s++; - if (*s == '"') { - quote = *s; - *(a++) = s++; - while (*s) { - if (*s == '\\' && *(s + 1) == quote) { - memmove(s, s + 1, strlen(s) + 1); - s++; - } - else if (*s == quote) { - s++; - break; - } - s++; - } - } - else if (*s) { - *(a++) = s; - while (*s && !ISSPACE(*s)) s++; - } - if (*s) - *s++ = '\0'; + if (!psa) { + sa.nLength = sizeof (SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + psa = &sa; } - *a = NULL; - exec = NULL; - if (argv[0]) { - exec = ALLOC_N(char, (strlen(argv[0]) + 1)); - if (argv[0][0] == '"' && argv[0][strlen(argv[0]) - 1] == '"') { - strcpy(exec, &argv[0][1]); - exec[strlen(exec) - 1] = '\0'; - } - else { - strcpy(exec, argv[0]); - } - if ((status = spawnvpe(mode, exec, argv, environ)) == -1) { - free(exec); - free(argv); - free(cmd2); - return -1; - } + + memset(&aStartupInfo, 0, sizeof (STARTUPINFO)); + memset(&aProcessInformation, 0, sizeof (PROCESS_INFORMATION)); + aStartupInfo.cb = sizeof (STARTUPINFO); + aStartupInfo.dwFlags = STARTF_USESTDHANDLES; + if (hInput) { + aStartupInfo.hStdInput = hInput; } - free(exec); - free(cmd2); - free(argv); - return (int)((status & 0xff) << 8); + else { + aStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + } + if (hOutput) { + aStartupInfo.hStdOutput = hOutput; + } + else { + aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + } + if (hError) { + aStartupInfo.hStdError = hError; + } + else { + aStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + } + + dwCreationFlags = (NORMAL_PRIORITY_CLASS); + + if ((shell = getenv("RUBYSHELL")) && NtHasRedirection(cmd)) { + char *tmp = ALLOCA_N(char, strlen(shell) + strlen(cmd) + sizeof (" -c ")); + sprintf(tmp, "%s -c %s", shell, cmd); + cmd = tmp; + } + else if ((shell = getenv("COMSPEC")) && + (NtHasRedirection(cmd) || isInternalCmd(cmd))) { + char *tmp = ALLOCA_N(char, strlen(shell) + strlen(cmd) + sizeof (" /c ")); + sprintf(tmp, "%s /c %s", shell, cmd); + cmd = tmp; + } + else { + shell = NULL; + } + + RUBY_CRITICAL({ + fRet = CreateProcess(shell, cmd, psa, psa, + psa->bInheritHandle, dwCreationFlags, NULL, NULL, + &aStartupInfo, &aProcessInformation); + errno = GetLastError(); + }); + + if (!fRet) { + child->pid = 0; /* release the slot */ + return NULL; + } + + CloseHandle(aProcessInformation.hThread); + + child->hProcess = aProcessInformation.hProcess; + child->pid = (pid_t)aProcessInformation.dwProcessId; + + if (!IsWinNT()) { + /* On Win9x, make pid positive similarly to cygwin and perl */ + child->pid = -child->pid; + } + + return child; } #endif @@ -1062,6 +865,7 @@ NtHasRedirection (char *cmd) case '>': case '<': + case '|': if (!inquote) return TRUE; @@ -2465,6 +2269,30 @@ void setservent (int stayopen) {} #define WNOHANG -1 #endif +static pid_t +wait_child(struct ChildRecord *child, int *stat_loc, DWORD timeout) +{ + DWORD exitcode; + + if (!GetExitCodeProcess(child->hProcess, &exitcode)) { + /* If an error occured, return immediatly. */ + errno = GetLastError(); + if (errno == ERROR_INVALID_PARAMETER) { + errno = ECHILD; + } + CloseChildHandle(child); + return -1; + } + if (exitcode != STILL_ACTIVE) { + /* If already died, return immediatly. */ + pid_t pid = child->pid; + CloseChildHandle(child); + if (stat_loc) *stat_loc = exitcode << 8; + return pid; + } + return 0; +} + pid_t waitpid (pid_t pid, int *stat_loc, int options) { @@ -2475,17 +2303,54 @@ waitpid (pid_t pid, int *stat_loc, int options) } else { timeout = INFINITE; } - RUBY_CRITICAL({ - if (wait_events((HANDLE)pid, timeout) == WAIT_OBJECT_0) { - pid = _cwait(stat_loc, pid, 0); + + if (pid == -1) { + int count = 0; + DWORD ret; + HANDLE events[MAXCHILDNUM + 1]; + + FOREACH_CHILD(child) { + if (!child->pid || child->pid < 0) continue; + if ((pid = wait_child(child, stat_loc, 0))) return pid; + events[count++] = child->hProcess; + } END_FOREACH_CHILD; + if (!count) { + errno = ECHILD; + return -1; } - else { - pid = 0; + events[count] = interrupted_event; + + ret = WaitForMultipleEvents(count, events, FALSE, timeout, TRUE); + if (ret == WAIT_TIMEOUT) return 0; + if ((ret -= WAIT_OBJECT_0) == count) { + ResetSignal(interrupted_event); + errno = EINTR; + return -1; } - }); -#if !defined __BORLANDC__ - if (pid) *stat_loc <<= 8; -#endif + if (ret > count) { + errno = GetLastError(); + return -1; + } + + return wait_child(ChildRecord + ret, stat_loc, 0); + } + else { + struct ChildRecord* child = FindChildSlot(pid); + if (!child) { + errno = ECHILD; + return -1; + } + + while (!(pid = wait_child(child, stat_loc, timeout))) { + /* wait... */ + if (wait_events(child->hProcess, timeout) != WAIT_OBJECT_0) { + /* still active */ + pid = 0; + break; + } + } + } + return pid; } @@ -2493,7 +2358,7 @@ waitpid (pid_t pid, int *stat_loc, int options) int _cdecl gettimeofday(struct timeval *tv, struct timezone *tz) -{ +{ SYSTEMTIME st; time_t t; struct tm tm; @@ -2559,39 +2424,56 @@ chown(const char *path, int owner, int group) } #include +#ifndef SIGINT +#define SIGINT 2 +#endif +#ifndef SIGKILL +#define SIGKILL 9 +#endif int kill(int pid, int sig) { - if ((unsigned int)pid == GetCurrentProcessId()) + int ret = 0; + + if ((unsigned int)pid == GetCurrentProcessId() && sig != SIGKILL) return raise(sig); - if (sig == 2 && pid > 0) { - if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, (DWORD)pid)) { - errno = GetLastError(); - return -1; - } + if (sig == SIGINT && pid > 0) { + RUBY_CRITICAL({ + if (!GenerateConsoleCtrlEvent(CTRL_C_EVENT, (DWORD)pid)) { + errno = GetLastError(); + ret = -1; + } + }); } - else if (sig == 9 && pid > 0) { + else if (sig == SIGKILL && pid > 0) { HANDLE hProc; - hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid); - if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) { - errno = GetLastError(); - return -1; - } - if (!TerminateProcess(hProc, 0)) { - errno = GetLastError(); + RUBY_CRITICAL({ + hProc = OpenProcess(PROCESS_TERMINATE, FALSE, + IsWin95() ? -pid : pid); + if (hProc == NULL || hProc == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + errno = ESRCH; + } + else { + errno = EPERM; + } + ret = -1; + } + else if (!TerminateProcess(hProc, 0)) { + errno = EPERM; + ret = -1; + } CloseHandle(hProc); - return -1; - } - CloseHandle(hProc); + }); } else { errno = EINVAL; - return -1; + ret = -1; } - return 0; + return ret; } int @@ -2609,14 +2491,16 @@ wait() char * win32_getenv(const char *name) { - char *curitem = NULL; /* XXX threadead */ - DWORD curlen = 0; /* XXX threadead */ + static char *curitem = NULL; + static DWORD curlen = 0; DWORD needlen; - curlen = 512; - curitem = ALLOC_N(char, curlen); + if (curitem == NULL || curlen == 0) { + curlen = 512; + curitem = ALLOC_N(char, curlen); + } - needlen = GetEnvironmentVariable(name,curitem,curlen); + needlen = GetEnvironmentVariable(name, curitem, curlen); if (needlen != 0) { while (needlen > curlen) { REALLOC_N(curitem, char, needlen); @@ -3070,3 +2954,35 @@ VALUE win32_asynchronize(asynchronous_func_t func, return val; } + +char **win32_get_environ(void) +{ + char *envtop, *env; + char **myenvtop, **myenv; + int num; + + envtop = GetEnvironmentStrings(); + for (env = envtop, num = 0; *env; env += strlen(env) + 1) + if (*env != '=') num++; + + myenvtop = ALLOC_N(char*, num + 1); + for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) { + if (*env != '=') { + *myenv = ALLOC_N(char, strlen(env) + 1); + strcpy(*myenv, env); + myenv++; + } + } + *myenv = NULL; + FreeEnvironmentStrings(envtop); + + return myenvtop; +} + +void win32_free_environ(char **env) +{ + char **t = env; + + while (*t) free(*t++); + free(env); +} diff --git a/win32/win32.h b/win32/win32.h index 94c8bd7845..0cf67f56a5 100644 --- a/win32/win32.h +++ b/win32/win32.h @@ -232,6 +232,8 @@ extern struct servent * mygetservbyname(char *, char *); extern struct servent * mygetservbyport(int, char *); extern char *win32_getenv(const char *); extern int myrename(const char *, const char *); +extern char **win32_get_environ(void); +extern void win32_free_environ(char **); extern int chown(const char *, int, int); extern int link(char *, char *);