2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
24 #include <afs/errmap_nt.h>
25 #include <afs/secutil_nt.h>
28 #include "pmgtprivate.h"
32 /* Signal disposition table and associated definitions and locks */
35 struct sigaction action; /* signal action information */
38 static sigtable_entry_t signalTable[NSIG]; /* signal table; slot 0 unused */
39 static pthread_mutex_t signalTableLock; /* lock protects signalTable */
41 /* Global signal block lock; all signal handlers are serialized for now */
42 static pthread_mutex_t signalBlockLock;
44 /* Named pipe prefix for sending signals */
45 #define PMGT_SIGNAL_PIPE_PREFIX "\\\\.\\pipe\\TransarcAfsSignalPipe"
47 /* Macro to test process exit status for an uncaught exception */
48 #define PMGT_IS_EXPSTATUS(status) (((status) & 0xF0000000) == 0xC0000000)
52 /* Child process table and associated definitions and locks */
55 HANDLE p_handle; /* process handle (NULL if table entry not valid) */
56 BOOL p_reserved; /* table entry is reserved for spawn */
57 DWORD p_id; /* process id */
58 BOOL p_terminated; /* process terminated; status available/valid */
59 DWORD p_status; /* status of terminated process */
62 /* Max number of active child processes supported */
63 #define PMGT_CHILD_MAX 100
65 static proctable_entry_t procTable[PMGT_CHILD_MAX]; /* child process table */
66 static int procEntryCount; /* count of valid entries in procTable */
67 static int procTermCount; /* count of terminated entries in procTable */
69 /* lock protects procTable, procEntryCount, and procTermCount */
70 static pthread_mutex_t procTableLock;
72 /* Named shared memory prefix for passing a data buffer to a child process */
73 #define PMGT_DATA_MEM_PREFIX "TransarcAfsSpawnDataMemory"
75 /* Named event prefix for indicating that a data buffer has been read */
76 #define PMGT_DATA_EVENT_PREFIX "TransarcAfsSpawnDataEvent"
78 /* event signals termination of a child process */
79 static pthread_cond_t childTermEvent;
82 /* Exported data values */
84 void *pmgt_spawnData = NULL;
85 size_t pmgt_spawnDataLen = 0;
88 /* General definitions */
90 #define DWORD_OF_ONES ((DWORD)0xFFFFFFFF) /* a common Win32 failure code */
97 /* ----------------- Signals ---------------- */
101 * SignalIsDefined() -- Determine if an integer value corresponds to a
102 * signal value defined for this platform.
105 SignalIsDefined(int signo)
109 if (signo >= 1 && signo <= (NSIG - 1)) {
110 /* value is in valid range; check specifics */
134 * DefaultActionHandler() -- Execute the default action for the given signal.
137 DefaultActionHandler(int signo)
146 /* default action is "exit" */
147 ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
154 /* default action is "core" */
155 /* Best we can do is to raise an exception that can be caught by
156 * Dr. Watson, which can in turn generate a crash dump file.
157 * The default exception handler will call ExitProcess() with
158 * our application-specific exception code.
160 RaiseException((DWORD)PMGT_SIGSTATUS_ENCODE(signo),
161 EXCEPTION_NONCONTINUABLE, 0, NULL);
164 /* default action is "ignore" */
167 /* default action is "stop" */
168 /* No good way to implement this from inside a process so ignore */
171 /* no default action for specified signal value; just ignore */
178 * ProcessSignal() -- Execute the specified or default handler for the given
179 * signal; reset the signal's disposition to SIG_DFL if necessary.
180 * If the signal's disposition is SIG_IGN then no processing takes place.
182 * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
185 ProcessSignal(int signo)
187 struct sigaction sigEntry;
189 if (signo != SIGKILL) {
190 /* serialize signals, but never block processing of SIGKILL */
191 (void) pthread_mutex_lock(&signalBlockLock);
194 /* fetch disposition of signo, updating it if necessary */
196 (void) pthread_mutex_lock(&signalTableLock);
197 sigEntry = signalTable[signo].action;
199 if ((sigEntry.sa_handler != SIG_IGN) &&
200 (sigEntry.sa_flags & SA_RESETHAND) &&
202 signalTable[signo].action.sa_handler = SIG_DFL;
204 (void) pthread_mutex_unlock(&signalTableLock);
206 /* execute handler */
208 if (sigEntry.sa_handler != SIG_IGN) {
209 if (sigEntry.sa_handler == SIG_DFL) {
210 sigEntry.sa_handler = DefaultActionHandler;
212 (*sigEntry.sa_handler)(signo);
215 if (signo != SIGKILL) {
216 (void) pthread_mutex_unlock(&signalBlockLock);
222 * RemoteSignalThread() -- Thread spawned to process remote signal.
224 * Param must be the signal number.
227 RemoteSignalThread(LPVOID param)
229 int signo = (int)param;
232 if (SignalIsDefined(signo)) {
234 ProcessSignal(signo);
235 } else if (signo != 0) {
236 /* invalid signal value */
244 * RemoteSignalListenerThread() -- Thread spawned to receive and process
245 * remotely generated signals; never returns.
247 * Param must be a handle for a duplex server message pipe in blocking
251 RemoteSignalListenerThread(LPVOID param)
253 HANDLE sigPipeHandle = (HANDLE)param;
256 /* wait for pipe client to connect */
258 if ((ConnectNamedPipe(sigPipeHandle, NULL)) ||
259 (GetLastError() == ERROR_PIPE_CONNECTED)) {
260 /* client connected; read signal value */
264 if ((ReadFile(sigPipeHandle,
265 &signo, sizeof(signo), &bytesXfered, NULL)) &&
266 (bytesXfered == sizeof(signo))) {
267 HANDLE sigThreadHandle;
270 /* ACK signal to release sender */
271 (void) WriteFile(sigPipeHandle,
272 &signo, sizeof(signo), &bytesXfered, NULL);
274 /* spawn thread to process signal; we do this so that
275 * we can always process a SIGKILL even if a signal handler
276 * invoked earlier fails to return (blocked/spinning).
279 CreateThread(NULL, /* default security attr. */
280 0, /* default stack size */
282 (LPVOID)signo, /* thread argument */
283 0, /* creation flags */
284 &sigThreadId); /* thread id */
286 if (sigThreadHandle != NULL) {
287 (void) CloseHandle(sigThreadHandle);
290 /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
293 /* connect failed; this should never happen */
294 Sleep(2000); /* sleep 2 seconds to avoid tight loop */
297 (void)DisconnectNamedPipe(sigPipeHandle);
309 * pmgt_SigactionSet() -- Examine and/or specify the action for a given
310 * signal (Unix sigaction() semantics).
313 pmgt_SigactionSet(int signo,
314 const struct sigaction *actionP,
315 struct sigaction *old_actionP)
317 /* validate arguments */
319 if (!SignalIsDefined(signo) || signo == SIGKILL) {
320 /* invalid signal value or signal can't be caught/ignored */
325 if (actionP && actionP->sa_handler == SIG_ERR) {
326 /* invalid signal disposition */
331 /* fetch and/or set disposition of signo */
333 (void) pthread_mutex_lock(&signalTableLock);
336 *old_actionP = signalTable[signo].action;
340 signalTable[signo].action = *actionP;
343 (void) pthread_mutex_unlock(&signalTableLock);
350 * pmgt_SignalSet() -- Specify the disposition for a given signal
351 * value (Unix signal() semantics).
353 void (__cdecl *pmgt_SignalSet(int signo,
354 void (__cdecl *dispP)(int)))(int)
356 struct sigaction newAction, oldAction;
358 /* construct action to request Unix signal() semantics */
360 newAction.sa_handler = dispP;
361 sigemptyset(&newAction.sa_mask);
362 newAction.sa_flags = SA_RESETHAND;
364 if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
365 /* successfully set new signal action */
366 return oldAction.sa_handler;
368 /* failed to set signal action; errno will have been set */
375 * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
379 pmgt_SignalRaiseLocal(int signo)
383 /* Process signal directly in the context of the calling thread.
384 * This is the same as if the signal had been raised in this process
385 * and this thread chosen to execute the handler.
388 if (SignalIsDefined(signo)) {
390 ProcessSignal(signo);
391 } else if (signo != 0) {
392 /* invalid signal value */
401 * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
402 * the signal is specified by name (C raise() semantics).
404 * Upon successful completion, *libSigno is set to the process management
405 * library's constant value for signame.
407 * Note: exists to implement the native-signal redirector (redirect_nt.c),
408 * which can't include procmgmt.h and hence can't get the SIG* decls.
411 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
416 if (!strcmp(signame, "SIGHUP")) {
418 } else if (!strcmp(signame, "SIGINT")) {
420 } else if (!strcmp(signame, "SIGQUIT")) {
422 } else if (!strcmp(signame, "SIGILL")) {
424 } else if (!strcmp(signame, "SIGABRT")) {
426 } else if (!strcmp(signame, "SIGFPE")) {
428 } else if (!strcmp(signame, "SIGKILL")) {
430 } else if (!strcmp(signame, "SIGSEGV")) {
432 } else if (!strcmp(signame, "SIGTERM")) {
434 } else if (!strcmp(signame, "SIGUSR1")) {
436 } else if (!strcmp(signame, "SIGUSR2")) {
438 } else if (!strcmp(signame, "SIGCLD")) {
440 } else if (!strcmp(signame, "SIGCHLD")) {
442 } else if (!strcmp(signame, "SIGTSTP")) {
445 /* unknown signal name */
452 rc = pmgt_SignalRaiseLocal(signo);
459 * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
462 * Note: only supports sending signal to a specific (single) process.
465 pmgt_SignalRaiseRemote(pid_t pid,
469 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
474 /* validate arguments */
476 if ((pid <= (pid_t)0) ||
477 (!SignalIsDefined(signo) && signo != 0)) {
478 /* invalid pid or signo */
483 /* optimize for the "this process" case */
485 if (pid == (pid_t)GetCurrentProcessId()) {
486 return pmgt_SignalRaiseLocal(signo);
489 /* send signal to process via named pipe */
491 sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
494 CallNamedPipe(sigPipeName, /* process pid's signal pipe */
495 &signo, /* data written to pipe */
496 sizeof(signo), /* size of data to write */
497 &signoACK, /* data read from pipe */
498 sizeof(signoACK), /* size of data read buffer */
499 &ackBytesRead, /* number of bytes actually read */
500 5 * 1000); /* 5 second timeout */
503 /* failed to send signal via named pipe */
506 if (signo == SIGKILL) {
507 /* could be a non-AFS process, which might still be kill-able */
510 if (procHandle = OpenProcess(PROCESS_TERMINATE,
511 FALSE, (DWORD)pid)) {
512 if (TerminateProcess(procHandle,
513 PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
514 /* successfully killed process */
517 errno = nterr_nt2unix(GetLastError(), EPERM);
519 (void) CloseHandle(procHandle);
521 if (GetLastError() == ERROR_INVALID_PARAMETER) {
523 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
526 errno = nterr_nt2unix(GetLastError(), EPERM);
530 /* couldn't open pipe so can't send (non-SIGKILL) signal */
531 errno = nterr_nt2unix(GetLastError(), EPERM);
541 /* ----------------- Processes ---------------- */
545 * StringArrayToString() -- convert a null-terminated array of strings,
546 * such as argv, into a single string of space-separated elements
547 * with each element quoted (in case it contains space characters
548 * or is of zero length).
551 StringArrayToString(char *strArray[])
557 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
558 /* sum all string lengths */
559 byteCount += strlen(strArray[strCount]);
562 /* put all strings into buffer; guarantee buffer is at least one char */
563 buffer = (char *)malloc(byteCount + (strCount * 3) /* quotes+space */ + 1);
564 if (buffer != NULL) {
569 for (i = 0; i < strCount; i++) {
570 char *bufp = buffer + strlen(buffer);
572 if (i == strCount - 1) {
573 /* last string; no trailing space */
574 sprintf(bufp, "\"%s\"", strArray[i]);
576 sprintf(bufp, "\"%s\" ", strArray[i]);
586 * StringArrayToMultiString() -- convert a null-terminated array of strings,
587 * such as envp, into a multistring.
590 StringArrayToMultiString(char *strArray[])
596 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
597 /* sum all string lengths */
598 byteCount += strlen(strArray[strCount]);
601 /* put all strings into buffer; guarantee buffer is at least two chars */
602 buffer = (char *)malloc(byteCount + strCount + 2);
603 if (buffer != NULL) {
604 if (byteCount == 0) {
611 for (i = 0; i < strCount; i++) {
612 int strLen = strlen(strArray[i]);
615 /* can not embed zero length string in a multistring */
616 strcpy(bufp, strArray[i]);
620 bufp = '\0'; /* terminate multistring */
630 * ComputeWaitStatus() -- Compute an appropriate wait status value from
631 * a given process termination (exit) code.
634 ComputeWaitStatus(DWORD exitStatus)
638 if (PMGT_IS_SIGSTATUS(exitStatus)) {
639 /* child terminated due to an unhandled signal */
640 int signo = PMGT_SIGSTATUS_DECODE(exitStatus);
641 waitStatus = WSIGNALED_ENCODE(signo);
642 } else if (PMGT_IS_EXPSTATUS(exitStatus)) {
643 /* child terminated due to an uncaught exception */
646 switch (exitStatus) {
647 case EXCEPTION_FLT_DENORMAL_OPERAND:
648 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
649 case EXCEPTION_FLT_INEXACT_RESULT:
650 case EXCEPTION_FLT_INVALID_OPERATION:
651 case EXCEPTION_FLT_OVERFLOW:
652 case EXCEPTION_FLT_STACK_CHECK:
653 case EXCEPTION_FLT_UNDERFLOW:
654 case EXCEPTION_INT_DIVIDE_BY_ZERO:
655 case EXCEPTION_INT_OVERFLOW:
658 case EXCEPTION_PRIV_INSTRUCTION:
659 case EXCEPTION_ILLEGAL_INSTRUCTION:
669 waitStatus = WSIGNALED_ENCODE(signo);
671 /* child terminated normally */
672 waitStatus = WEXITED_ENCODE(exitStatus);
681 * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
682 * a child process, along with a corresponding buffer read event.
684 * ASSUMPTIONS: child process is linked with this process management library;
685 * otherwise no data transfer will take place.
688 CreateChildDataBuffer(DWORD pid, /* child pid */
689 void *datap, /* data to place in buffer */
690 size_t dataLen, /* size of data in bytes */
691 HANDLE *bufMemHandlep, /* buffer memory handle */
692 HANDLE *bufEventHandlep) /* buffer read event handle */
694 BOOL fsuccess = FALSE;
695 DWORD bufMemSize = dataLen + sizeof(size_t);
696 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
697 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
699 sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
700 sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
702 /* Create and initialize named shared memory and named event */
705 CreateFileMapping((HANDLE)0xFFFFFFFF, /* page-file backed */
712 if (*bufMemHandlep != NULL) {
715 bufMemp = MapViewOfFile(*bufMemHandlep,
716 FILE_MAP_WRITE, 0, 0, bufMemSize);
718 if (bufMemp != NULL) {
719 /* copy data into shared memory, prefixed with data size */
720 size_t *memp = (size_t *)bufMemp;
723 memcpy((void *)memp, datap, dataLen);
725 if (UnmapViewOfFile(bufMemp)) {
726 /* create buffer read event */
727 *bufEventHandlep = CreateEvent(NULL,
728 FALSE /* manual reset */,
729 FALSE /* initial state */,
731 if (*bufEventHandlep != NULL) {
738 (void) CloseHandle(*bufMemHandlep);
743 *bufMemHandlep = *bufEventHandlep = NULL;
751 * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
752 * if any, and place in allocated storage.
755 ReadChildDataBuffer(void **datap, /* allocated data buffer */
756 size_t *dataLen) /* size of data buffer returned */
758 BOOL fsuccess = FALSE;
759 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
760 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
761 HANDLE bufMemHandle, bufEventHandle;
763 sprintf(bufMemName, "%s%d",
764 PMGT_DATA_MEM_PREFIX, (int)GetCurrentProcessId());
765 sprintf(bufEventName, "%s%d",
766 PMGT_DATA_EVENT_PREFIX, (int)GetCurrentProcessId());
768 /* Attempt to open named event and named shared memory */
770 bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
772 if (bufEventHandle != NULL) {
773 bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
775 if (bufMemHandle != NULL) {
778 bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
780 if (bufMemp != NULL) {
781 /* read data size and data from shared memory */
782 size_t *memp = (size_t *)bufMemp;
785 *datap = (void *)malloc(*dataLen);
787 if (*datap != NULL) {
788 memcpy(*datap, (void *)memp, *dataLen);
791 (void) UnmapViewOfFile(bufMemp);
794 (void) CloseHandle(bufMemHandle);
797 (void) SetEvent(bufEventHandle);
798 (void) CloseHandle(bufEventHandle);
811 * ChildMonitorThread() -- Thread spawned to monitor status of child process.
813 * Param must be index into child process table.
816 ChildMonitorThread(LPVOID param)
818 int tidx = (int)param;
819 HANDLE childProcHandle;
823 /* retrieve handle for child process from process table and duplicate */
825 (void) pthread_mutex_lock(&procTableLock);
828 DuplicateHandle(GetCurrentProcess(), /* source process handle */
829 procTable[tidx].p_handle, /* source handle to dup */
830 GetCurrentProcess(), /* target process handle */
831 &childProcHandle, /* target handle (duplicate) */
832 0, /* access (ignored here) */
833 FALSE, /* not inheritable */
834 DUPLICATE_SAME_ACCESS);
836 (void) pthread_mutex_unlock(&procTableLock);
839 /* wait for child process to terminate */
841 if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
842 /* child process terminated; mark in table and signal event */
843 (void) pthread_mutex_lock(&procTableLock);
845 procTable[tidx].p_terminated = TRUE;
846 (void) GetExitCodeProcess(childProcHandle,
847 &procTable[tidx].p_status);
850 (void) pthread_mutex_unlock(&procTableLock);
852 (void) pthread_cond_broadcast(&childTermEvent);
854 /* process/raise SIGCHLD; do last in case handler never returns */
855 ProcessSignal(SIGCHLD);
859 (void) CloseHandle(childProcHandle);
862 /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
863 * fail; however, this should never happen.
871 * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
873 * Returns pid of the child process ((pid_t)-1 on failure with errno set).
875 * Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
876 * Open files are not inherited; child's stdin, stdout, and stderr
877 * are set to parent's console.
878 * If spath does not specify a filename extension ".exe" is used.
879 * If sdatap is not NULL, and sdatalen > 0, data is passed to child.
880 * The spath and sargv[] strings must not contain quote chars (").
882 * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
885 pmgt_ProcessSpawnVEB(const char *spath,
892 char *pathbuf, *argbuf, *envbuf;
893 char pathext[_MAX_EXT];
894 STARTUPINFO startInfo;
895 PROCESS_INFORMATION procInfo;
896 HANDLE monitorHandle = NULL;
897 HANDLE bufMemHandle, bufEventHandle;
898 DWORD monitorId, createFlags;
899 BOOL passingBuffer = (sdatap != NULL && sdatalen > 0);
902 /* verify arguments */
903 if (!spath || !sargv) {
906 } else if (*spath == '\0') {
911 /* create path with .exe extension if no filename extension supplied */
912 if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */))) {
916 strcpy(pathbuf, spath);
918 _splitpath(pathbuf, NULL, NULL, NULL, pathext);
919 if (*pathext == '\0') {
920 /* no filename extension supplied for spath; .exe is assumed */
921 strcat(pathbuf, ".exe");
924 /* create command line argument string */
925 argbuf = StringArrayToString(sargv);
933 /* create environment variable block (multistring) */
935 /* use environment variables provided */
936 envbuf = StringArrayToMultiString(senvp);
945 /* use default environment variables */
949 /* set process creation flags */
950 createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
952 if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
953 createFlags |= DETACHED_PROCESS;
956 /* clear start-up info; use defaults */
957 memset((void *)&startInfo, 0, sizeof(startInfo));
958 startInfo.cb = sizeof(startInfo);
960 /* perform the following as a logically atomic unit:
961 * 1) allocate a process table entry
962 * 2) spawn child process (suspended)
963 * 3) create data buffer to pass (optional)
964 * 4) initialize process table entry
965 * 5) start child watcher thread
966 * 6) resume spawned child process
969 (void) pthread_mutex_lock(&procTableLock);
971 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
972 if (procTable[tidx].p_handle == NULL &&
973 procTable[tidx].p_reserved == FALSE) {
974 procTable[tidx].p_reserved = TRUE;
978 (void) pthread_mutex_unlock(&procTableLock);
980 if (tidx >= PMGT_CHILD_MAX) {
981 /* no space left in process table */
991 CreateProcess(pathbuf, /* executable path */
992 argbuf, /* command line argument string */
993 NULL, /* default process security attr */
994 NULL, /* default thread security attr */
995 FALSE, /* do NOT inherit handles */
996 createFlags, /* creation control flags */
997 envbuf, /* environment variable block */
998 NULL, /* current directory is that of parent */
999 &startInfo, /* startup info block */
1007 /* failed to spawn process */
1008 errno = nterr_nt2unix(GetLastError(), ENOENT);
1010 (void) pthread_mutex_lock(&procTableLock);
1011 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
1012 (void) pthread_mutex_unlock(&procTableLock);
1017 if (passingBuffer) {
1018 /* create named data buffer and read event for child */
1019 fsuccess = CreateChildDataBuffer(procInfo.dwProcessId,
1025 (void) pthread_mutex_lock(&procTableLock);
1026 procTable[tidx].p_reserved = FALSE; /* mark entry not reserved */
1027 (void) pthread_mutex_unlock(&procTableLock);
1029 (void) TerminateProcess(procInfo.hProcess,
1030 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1031 (void) CloseHandle(procInfo.hThread);
1032 (void) CloseHandle(procInfo.hProcess);
1039 (void) pthread_mutex_lock(&procTableLock);
1041 procTable[tidx].p_handle = procInfo.hProcess;
1042 procTable[tidx].p_id = procInfo.dwProcessId;
1043 procTable[tidx].p_terminated = FALSE;
1047 /* Note: must hold procTableLock during monitor thread creation so
1048 * that if creation fails we can clean up process table before another
1049 * thread has a chance to see this procTable entry. Continue to hold
1050 * procTableLock while resuming child process, since the procTable
1051 * entry contains a copy of the child process handle which we might use.
1054 CreateThread(NULL, /* default security attr. */
1055 0, /* default stack size */
1057 (LPVOID)tidx, /* thread argument */
1058 0, /* creation flags */
1059 &monitorId); /* thread id */
1061 if (monitorHandle == NULL) {
1062 /* failed to start child monitor thread */
1063 procTable[tidx].p_handle = NULL; /* invalidate table entry */
1064 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
1067 (void) pthread_mutex_unlock(&procTableLock);
1069 (void) TerminateProcess(procInfo.hProcess,
1070 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1071 (void) CloseHandle(procInfo.hThread);
1072 (void) CloseHandle(procInfo.hProcess);
1074 if (passingBuffer) {
1075 (void) CloseHandle(bufMemHandle);
1076 (void) CloseHandle(bufEventHandle);
1083 /* Resume child process, which was created suspended to implement spawn
1084 * atomically. If resumption fails, which it never should, terminate
1085 * the child process with a status of SIGKILL. Spawn still succeeds and
1086 * the net result is the same as if the child process received a spurious
1087 * SIGKILL signal; the child monitor thread will then handle this.
1089 if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1090 (void) TerminateProcess(procInfo.hProcess,
1091 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1093 if (passingBuffer) {
1094 /* child will never read data buffer */
1095 (void) SetEvent(bufEventHandle);
1099 (void) pthread_mutex_unlock(&procTableLock);
1101 (void) CloseHandle(procInfo.hThread);
1102 (void) CloseHandle(monitorHandle);
1104 /* After spawn returns, signals can not be sent to the new child process
1105 * until that child initializes its signal-receiving mechanism (assuming
1106 * the child is linked with this library). Shorten (but sadly don't
1107 * eliminate) this window of opportunity for failure by yielding this
1108 * thread's time slice.
1110 (void) SwitchToThread();
1112 /* If passing a data buffer to child, wait until child reads buffer
1113 * before closing handles and thus freeing resources; if don't wait
1114 * then parent can not safely exit immediately after returning from
1115 * this call (hence why wait is not done in a background thread).
1117 if (passingBuffer) {
1118 WaitForSingleObject(bufEventHandle, 10000);
1119 /* note: if wait times out, child may not get to read buffer */
1120 (void) CloseHandle(bufMemHandle);
1121 (void) CloseHandle(bufEventHandle);
1124 return (pid_t)procInfo.dwProcessId;
1130 * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1131 * for child to terminate (Unix waitpid() semantics).
1133 * Note: does not support waiting for process in group (i.e., pid
1134 * equals (pid_t)0 or pid is less than (pid_t)-1.
1137 pmgt_ProcessWaitPid(pid_t pid,
1143 BOOL statusFound = FALSE;
1146 /* validate arguments */
1147 if (pid < (pid_t)-1 || pid == (pid_t)0) {
1152 /* determine how long caller is willing to wait for child */
1154 waitTime = (options & WNOHANG) ? 0 : INFINITE;
1156 /* get child status */
1158 (void) pthread_mutex_lock(&procTableLock);
1161 BOOL waitForChild = FALSE;
1163 if (procEntryCount == 0) {
1164 /* no child processes */
1168 /* determine if status is available for specified child id */
1170 if (pid == (pid_t)-1) {
1171 /* CASE 1: pid matches any child id */
1173 if (procTermCount == 0) {
1174 /* status not available for any child ... */
1175 if (waitTime == 0) {
1176 /* ... and caller is not willing to wait */
1179 /* ... but caller is willing to wait */
1180 waitForChild = TRUE;
1183 /* status available for some child; locate table entry */
1184 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1185 if (procTable[tidx].p_handle != NULL &&
1186 procTable[tidx].p_terminated == TRUE) {
1193 /* should never happen; indicates a bug */
1194 errno = EINTR; /* plausible lie for failure */
1200 /* CASE 2: pid must match a specific child id */
1202 /* locate table entry */
1203 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1204 if (procTable[tidx].p_handle != NULL &&
1205 procTable[tidx].p_id == (DWORD)pid) {
1210 if (tidx >= PMGT_CHILD_MAX) {
1211 /* pid does not match any child id */
1214 } else if (procTable[tidx].p_terminated == FALSE) {
1215 /* status not available for specified child ... */
1216 if (waitTime == 0) {
1217 /* ... and caller is not willing to wait */
1220 /* ... but caller is willing to wait */
1221 waitForChild = TRUE;
1224 /* status is available for specified child */
1231 (void) pthread_cond_wait(&childTermEvent, &procTableLock);
1238 /* child status available */
1240 *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1242 rc = (pid_t)procTable[tidx].p_id;
1244 /* clean up process table */
1245 (void) CloseHandle(procTable[tidx].p_handle);
1246 procTable[tidx].p_handle = NULL;
1247 procTable[tidx].p_reserved = FALSE;
1253 (void) pthread_mutex_unlock(&procTableLock);
1262 /* ----------------- General ---------------- */
1267 * PmgtLibraryInitialize() -- Initialize process management library.
1270 PmgtLibraryInitialize(void)
1273 HANDLE sigPipeHandle;
1274 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1275 HANDLE sigListenerHandle;
1276 DWORD sigListenerId;
1278 /* initialize mutex locks and condition variables */
1280 if ((rc = pthread_mutex_init(&signalTableLock, NULL)) ||
1281 (rc = pthread_mutex_init(&signalBlockLock, NULL)) ||
1282 (rc = pthread_mutex_init(&procTableLock, NULL)) ||
1283 (rc = pthread_cond_init(&childTermEvent, NULL))) {
1288 /* initialize signal disposition table */
1290 for (i = 0; i < NSIG; i++) {
1291 if (SignalIsDefined(i)) {
1292 /* initialize to default action for defined signals */
1293 signalTable[i].action.sa_handler = SIG_DFL;
1294 sigemptyset(&signalTable[i].action.sa_mask);
1295 signalTable[i].action.sa_flags = 0;
1297 /* initialize to ignore for undefined signals */
1298 signalTable[i].action.sa_handler = SIG_IGN;
1302 /* initialize child process table */
1304 for (i = 0; i < PMGT_CHILD_MAX; i++) {
1305 procTable[i].p_handle = NULL;
1306 procTable[i].p_reserved = FALSE;
1311 /* retrieve data buffer passed from parent in spawn, if any */
1313 if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1314 pmgt_spawnData = NULL;
1315 pmgt_spawnDataLen = 0;
1318 /* create named pipe for delivering signals to this process */
1320 sprintf(sigPipeName, "%s%d",
1321 PMGT_SIGNAL_PIPE_PREFIX, (int)GetCurrentProcessId());
1324 CreateNamedPipe(sigPipeName, /* pipe for this process */
1325 PIPE_ACCESS_DUPLEX | /* full duplex pipe */
1326 WRITE_DAC, /* DACL write access */
1327 PIPE_TYPE_MESSAGE | /* message type pipe */
1328 PIPE_READMODE_MESSAGE | /* message read-mode */
1329 PIPE_WAIT, /* blocking mode */
1330 1, /* max of 1 pipe instance */
1331 64, /* output buffer size (advisory) */
1332 64, /* input buffer size (advisory) */
1333 1000, /* 1 sec default client timeout */
1334 NULL); /* default security attr. */
1336 if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1337 /* failed to create signal pipe */
1338 errno = nterr_nt2unix(GetLastError(), EIO);
1342 /* add entry to signal pipe ACL granting local Administrators R/W access */
1344 (void) ObjectDaclEntryAdd(sigPipeHandle,
1346 LocalAdministratorsGroup,
1347 GENERIC_READ | GENERIC_WRITE,
1351 /* start signal pipe listener thread */
1354 CreateThread(NULL, /* default security attr. */
1355 0, /* default stack size */
1356 RemoteSignalListenerThread,
1357 (LPVOID)sigPipeHandle, /* thread argument */
1358 0, /* creation flags */
1359 &sigListenerId); /* thread id */
1361 if (sigListenerHandle != NULL) {
1362 /* listener thread started; bump priority */
1363 (void) SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1364 (void) CloseHandle(sigListenerHandle);
1366 /* failed to start listener thread */
1368 (void) CloseHandle(sigPipeHandle);
1372 /* redirect native NT signals into this process management library */
1374 if (pmgt_RedirectNativeSignals()) {
1375 /* errno set by called function */
1384 * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1385 * function in the MSVC runtime DLL (msvcrt.dll).
1387 * Note: the system serializes calls to this function.
1390 DllMain(HINSTANCE dllInstHandle, /* instance handle for this DLL module */
1391 DWORD reason, /* reason function is being called */
1392 LPVOID reserved) /* reserved for future use */
1394 if (reason == DLL_PROCESS_ATTACH) {
1395 /* library is being attached to a process */
1396 if (PmgtLibraryInitialize()) {
1397 /* failed to initialize library */
1401 /* disable thread attach/detach notifications */
1402 (void) DisableThreadLibraryCalls(dllInstHandle);