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>
14 #include "procmgmt.h" /* Must be before roken */
20 #include <afs/errmap_nt.h>
21 #include <afs/secutil_nt.h>
23 #include "pmgtprivate.h"
25 /* Signal disposition table and associated definitions and locks */
28 struct sigaction action; /* signal action information */
31 static sigtable_entry_t signalTable[NSIG]; /* signal table; slot 0 unused */
32 static pthread_mutex_t signalTableLock; /* lock protects signalTable */
34 /* Global signal block lock; all signal handlers are serialized for now */
35 static pthread_mutex_t signalBlockLock;
37 /* Named pipe prefix for sending signals */
38 #define PMGT_SIGNAL_PIPE_PREFIX "\\\\.\\pipe\\TransarcAfsSignalPipe"
40 /* Macro to test process exit status for an uncaught exception */
41 #define PMGT_IS_EXPSTATUS(status) (((status) & 0xF0000000) == 0xC0000000)
45 /* Child process table and associated definitions and locks */
48 HANDLE p_handle; /* process handle (NULL if table entry not valid) */
49 BOOL p_reserved; /* table entry is reserved for spawn */
50 DWORD p_id; /* process id */
51 BOOL p_terminated; /* process terminated; status available/valid */
52 DWORD p_status; /* status of terminated process */
55 /* Max number of active child processes supported */
56 #define PMGT_CHILD_MAX 100
58 static proctable_entry_t procTable[PMGT_CHILD_MAX]; /* child process table */
59 static int procEntryCount; /* count of valid entries in procTable */
60 static int procTermCount; /* count of terminated entries in procTable */
62 /* lock protects procTable, procEntryCount, and procTermCount */
63 static pthread_mutex_t procTableLock;
65 /* Named shared memory prefix for passing a data buffer to a child process */
66 #define PMGT_DATA_MEM_PREFIX "TransarcAfsSpawnDataMemory"
68 /* Named event prefix for indicating that a data buffer has been read */
69 #define PMGT_DATA_EVENT_PREFIX "TransarcAfsSpawnDataEvent"
71 /* event signals termination of a child process */
72 static pthread_cond_t childTermEvent;
75 /* Exported data values */
77 void *pmgt_spawnData = NULL;
78 size_t pmgt_spawnDataLen = 0;
81 /* General definitions */
83 #define DWORD_OF_ONES ((DWORD)0xFFFFFFFF) /* a common Win32 failure code */
90 /* ----------------- Signals ---------------- */
94 * SignalIsDefined() -- Determine if an integer value corresponds to a
95 * signal value defined for this platform.
98 SignalIsDefined(int signo)
102 if (signo >= 1 && signo <= (NSIG - 1)) {
103 /* value is in valid range; check specifics */
127 * DefaultActionHandler() -- Execute the default action for the given signal.
130 DefaultActionHandler(int signo)
138 /* default action is "exit" */
139 ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
145 /* default action is "core" */
146 /* Best we can do is to raise an exception that can be caught by
147 * Dr. Watson, which can in turn generate a crash dump file.
148 * The default exception handler will call ExitProcess() with
149 * our application-specific exception code.
151 RaiseException((DWORD) PMGT_SIGSTATUS_ENCODE(signo),
152 EXCEPTION_NONCONTINUABLE, 0, NULL);
157 /* default action is "ignore" */
160 /* default action is "stop" */
161 /* No good way to implement this from inside a process so ignore */
164 /* no default action for specified signal value; just ignore */
171 * ProcessSignal() -- Execute the specified or default handler for the given
172 * signal; reset the signal's disposition to SIG_DFL if necessary.
173 * If the signal's disposition is SIG_IGN then no processing takes place.
175 * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
178 ProcessSignal(int signo)
180 struct sigaction sigEntry;
182 if (signo != SIGKILL) {
183 /* serialize signals, but never block processing of SIGKILL */
184 (void)pthread_mutex_lock(&signalBlockLock);
187 /* fetch disposition of signo, updating it if necessary */
189 (void)pthread_mutex_lock(&signalTableLock);
190 sigEntry = signalTable[signo].action;
192 if ((sigEntry.sa_handler != SIG_IGN) && (sigEntry.sa_flags & SA_RESETHAND)
193 && (signo != SIGILL)) {
194 signalTable[signo].action.sa_handler = SIG_DFL;
196 (void)pthread_mutex_unlock(&signalTableLock);
198 /* execute handler */
200 if (sigEntry.sa_handler != SIG_IGN) {
201 if (sigEntry.sa_handler == SIG_DFL) {
202 sigEntry.sa_handler = DefaultActionHandler;
204 (*sigEntry.sa_handler) (signo);
207 if (signo != SIGKILL) {
208 (void)pthread_mutex_unlock(&signalBlockLock);
214 * RemoteSignalThread() -- Thread spawned to process remote signal.
216 * Param must be the signal number.
219 RemoteSignalThread(LPVOID param)
221 int signo = (int)(intptr_t)param;
224 if (SignalIsDefined(signo)) {
226 ProcessSignal(signo);
227 } else if (signo != 0) {
228 /* invalid signal value */
236 * RemoteSignalListenerThread() -- Thread spawned to receive and process
237 * remotely generated signals; never returns.
239 * Param must be a handle for a duplex server message pipe in blocking
243 RemoteSignalListenerThread(LPVOID param)
245 HANDLE sigPipeHandle = (HANDLE) param;
246 HMODULE hLib = LoadLibrary("AFSPROCMGMT.DLL");
249 /* wait for pipe client to connect */
251 if ((ConnectNamedPipe(sigPipeHandle, NULL))
252 || (GetLastError() == ERROR_PIPE_CONNECTED)) {
253 /* client connected; read signal value */
258 (sigPipeHandle, &signo, sizeof(signo), &bytesXfered, NULL))
259 && (bytesXfered == sizeof(signo))) {
260 HANDLE sigThreadHandle;
263 /* ACK signal to release sender */
264 (void)WriteFile(sigPipeHandle, &signo, sizeof(signo),
267 /* spawn thread to process signal; we do this so that
268 * we can always process a SIGKILL even if a signal handler
269 * invoked earlier fails to return (blocked/spinning).
271 sigThreadHandle = CreateThread(NULL, /* default security attr. */
272 0, /* default stack size */
273 RemoteSignalThread, (LPVOID) (intptr_t)signo, /* thread argument */
274 0, /* creation flags */
275 &sigThreadId); /* thread id */
277 if (sigThreadHandle != NULL) {
278 (void)CloseHandle(sigThreadHandle);
281 /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
284 /* connect failed; this should never happen */
285 Sleep(2000); /* sleep 2 seconds to avoid tight loop */
288 (void)DisconnectNamedPipe(sigPipeHandle);
301 * pmgt_SigactionSet() -- Examine and/or specify the action for a given
302 * signal (Unix sigaction() semantics).
305 pmgt_SigactionSet(int signo, const struct sigaction *actionP,
306 struct sigaction *old_actionP)
308 /* validate arguments */
310 if (!SignalIsDefined(signo) || signo == SIGKILL) {
311 /* invalid signal value or signal can't be caught/ignored */
316 if (actionP && actionP->sa_handler == SIG_ERR) {
317 /* invalid signal disposition */
322 /* fetch and/or set disposition of signo */
324 (void)pthread_mutex_lock(&signalTableLock);
327 *old_actionP = signalTable[signo].action;
331 signalTable[signo].action = *actionP;
334 (void)pthread_mutex_unlock(&signalTableLock);
341 * pmgt_SignalSet() -- Specify the disposition for a given signal
342 * value (Unix signal() semantics).
344 void (__cdecl * pmgt_SignalSet(int signo, void (__cdecl * dispP) (int))) (int) {
345 struct sigaction newAction, oldAction;
347 /* construct action to request Unix signal() semantics */
349 newAction.sa_handler = dispP;
350 sigemptyset(&newAction.sa_mask);
351 newAction.sa_flags = SA_RESETHAND;
353 if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
354 /* successfully set new signal action */
355 return oldAction.sa_handler;
357 /* failed to set signal action; errno will have been set */
364 * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
368 pmgt_SignalRaiseLocal(int signo)
372 /* Process signal directly in the context of the calling thread.
373 * This is the same as if the signal had been raised in this process
374 * and this thread chosen to execute the handler.
377 if (SignalIsDefined(signo)) {
379 ProcessSignal(signo);
380 } else if (signo != 0) {
381 /* invalid signal value */
390 * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
391 * the signal is specified by name (C raise() semantics).
393 * Upon successful completion, *libSigno is set to the process management
394 * library's constant value for signame.
396 * Note: exists to implement the native-signal redirector (redirect_nt.c),
397 * which can't include procmgmt.h and hence can't get the SIG* decls.
400 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
405 if (!strcmp(signame, "SIGHUP")) {
407 } else if (!strcmp(signame, "SIGINT")) {
409 } else if (!strcmp(signame, "SIGQUIT")) {
411 } else if (!strcmp(signame, "SIGILL")) {
413 } else if (!strcmp(signame, "SIGABRT")) {
415 } else if (!strcmp(signame, "SIGFPE")) {
417 } else if (!strcmp(signame, "SIGKILL")) {
419 } else if (!strcmp(signame, "SIGSEGV")) {
421 } else if (!strcmp(signame, "SIGTERM")) {
423 } else if (!strcmp(signame, "SIGUSR1")) {
425 } else if (!strcmp(signame, "SIGUSR2")) {
427 } else if (!strcmp(signame, "SIGCLD")) {
429 } else if (!strcmp(signame, "SIGCHLD")) {
431 } else if (!strcmp(signame, "SIGTSTP")) {
434 /* unknown signal name */
441 rc = pmgt_SignalRaiseLocal(signo);
448 * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
451 * Note: only supports sending signal to a specific (single) process.
454 pmgt_SignalRaiseRemote(pid_t pid, int signo)
457 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
462 /* validate arguments */
464 if ((pid <= (pid_t) 0) || (!SignalIsDefined(signo) && signo != 0)) {
465 /* invalid pid or signo */
470 /* optimize for the "this process" case */
472 if (pid == (pid_t) GetCurrentProcessId()) {
473 return pmgt_SignalRaiseLocal(signo);
476 /* send signal to process via named pipe */
478 sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
480 fsuccess = CallNamedPipe(sigPipeName, /* process pid's signal pipe */
481 &signo, /* data written to pipe */
482 sizeof(signo), /* size of data to write */
483 &signoACK, /* data read from pipe */
484 sizeof(signoACK), /* size of data read buffer */
485 &ackBytesRead, /* number of bytes actually read */
486 5 * 1000); /* 5 second timeout */
489 /* failed to send signal via named pipe */
492 if (signo == SIGKILL) {
493 /* could be a non-AFS process, which might still be kill-able */
497 OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid)) {
499 (procHandle, PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
500 /* successfully killed process */
503 errno = nterr_nt2unix(GetLastError(), EPERM);
505 (void)CloseHandle(procHandle);
507 if (GetLastError() == ERROR_INVALID_PARAMETER) {
509 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
512 errno = nterr_nt2unix(GetLastError(), EPERM);
516 /* couldn't open pipe so can't send (non-SIGKILL) signal */
517 errno = nterr_nt2unix(GetLastError(), EPERM);
527 /* ----------------- Processes ---------------- */
531 * StringArrayToString() -- convert a null-terminated array of strings,
532 * such as argv, into a single string of space-separated elements
533 * with each element quoted (in case it contains space characters
534 * or is of zero length).
537 StringArrayToString(char *strArray[])
543 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
544 /* sum all string lengths */
545 byteCount += (int)strlen(strArray[strCount]);
548 /* put all strings into buffer; guarantee buffer is at least one char */
549 buffer = (char *)malloc(byteCount + (strCount * 3) /* quotes+space */ +1);
550 if (buffer != NULL) {
555 for (i = 0; i < strCount; i++) {
556 char *bufp = buffer + strlen(buffer);
558 if (i == strCount - 1) {
559 /* last string; no trailing space */
560 sprintf(bufp, "\"%s\"", strArray[i]);
562 sprintf(bufp, "\"%s\" ", strArray[i]);
572 * StringArrayToMultiString() -- convert a null-terminated array of strings,
573 * such as envp, into a multistring.
576 StringArrayToMultiString(char *strArray[])
582 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
583 /* sum all string lengths */
584 byteCount += strlen(strArray[strCount]);
587 /* put all strings into buffer; guarantee buffer is at least two chars */
588 buffer = (char *)malloc(byteCount + strCount + 2);
589 if (buffer != NULL) {
590 if (byteCount == 0) {
597 for (i = 0; i < strCount; i++) {
598 int strLen = strlen(strArray[i]);
601 /* can not embed zero length string in a multistring */
602 strcpy(bufp, strArray[i]);
606 *bufp = '\0'; /* terminate multistring */
616 * ComputeWaitStatus() -- Compute an appropriate wait status value from
617 * a given process termination (exit) code.
620 ComputeWaitStatus(DWORD exitStatus)
624 if (PMGT_IS_SIGSTATUS(exitStatus)) {
625 /* child terminated due to an unhandled signal */
626 int signo = PMGT_SIGSTATUS_DECODE(exitStatus);
627 waitStatus = WSIGNALED_ENCODE(signo);
628 } else if (PMGT_IS_EXPSTATUS(exitStatus)) {
629 /* child terminated due to an uncaught exception */
632 switch (exitStatus) {
633 case EXCEPTION_FLT_DENORMAL_OPERAND:
634 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
635 case EXCEPTION_FLT_INEXACT_RESULT:
636 case EXCEPTION_FLT_INVALID_OPERATION:
637 case EXCEPTION_FLT_OVERFLOW:
638 case EXCEPTION_FLT_STACK_CHECK:
639 case EXCEPTION_FLT_UNDERFLOW:
640 case EXCEPTION_INT_DIVIDE_BY_ZERO:
641 case EXCEPTION_INT_OVERFLOW:
644 case EXCEPTION_PRIV_INSTRUCTION:
645 case EXCEPTION_ILLEGAL_INSTRUCTION:
655 waitStatus = WSIGNALED_ENCODE(signo);
657 /* child terminated normally */
658 waitStatus = WEXITED_ENCODE(exitStatus);
667 * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
668 * a child process, along with a corresponding buffer read event.
670 * ASSUMPTIONS: child process is linked with this process management library;
671 * otherwise no data transfer will take place.
674 CreateChildDataBuffer(DWORD pid, /* child pid */
675 void *datap, /* data to place in buffer */
676 size_t dataLen, /* size of data in bytes */
677 HANDLE * bufMemHandlep, /* buffer memory handle */
678 HANDLE * bufEventHandlep)
679 { /* buffer read event handle */
680 BOOL fsuccess = FALSE;
681 DWORD bufMemSize = dataLen + (DWORD)sizeof(size_t);
682 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
683 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
685 sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
686 sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
688 /* Create and initialize named shared memory and named event */
690 *bufMemHandlep = CreateFileMapping(INVALID_HANDLE_VALUE, /* page-file backed */
691 NULL, PAGE_READWRITE, 0, bufMemSize,
694 if (*bufMemHandlep != NULL) {
698 MapViewOfFile(*bufMemHandlep, FILE_MAP_WRITE, 0, 0, bufMemSize);
700 if (bufMemp != NULL) {
701 /* copy data into shared memory, prefixed with data size */
702 size_t *memp = (size_t *) bufMemp;
705 memcpy((void *)memp, datap, dataLen);
707 if (UnmapViewOfFile(bufMemp)) {
708 /* create buffer read event */
710 CreateEvent(NULL, FALSE /* manual reset */ ,
711 FALSE /* initial state */ ,
713 if (*bufEventHandlep != NULL) {
720 (void)CloseHandle(*bufMemHandlep);
725 *bufMemHandlep = *bufEventHandlep = NULL;
733 * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
734 * if any, and place in allocated storage.
737 ReadChildDataBuffer(void **datap, /* allocated data buffer */
739 { /* size of data buffer returned */
740 BOOL fsuccess = FALSE;
741 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
742 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
743 HANDLE bufMemHandle, bufEventHandle;
745 sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX,
746 (int)GetCurrentProcessId());
747 sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX,
748 (int)GetCurrentProcessId());
750 /* Attempt to open named event and named shared memory */
752 bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
754 if (bufEventHandle != NULL) {
755 bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
757 if (bufMemHandle != NULL) {
760 bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
762 if (bufMemp != NULL) {
763 /* read data size and data from shared memory */
764 size_t *memp = (size_t *) bufMemp;
767 *datap = (void *)malloc(*dataLen);
769 if (*datap != NULL) {
770 memcpy(*datap, (void *)memp, *dataLen);
773 (void)UnmapViewOfFile(bufMemp);
776 (void)CloseHandle(bufMemHandle);
779 (void)SetEvent(bufEventHandle);
780 (void)CloseHandle(bufEventHandle);
793 * ChildMonitorThread() -- Thread spawned to monitor status of child process.
795 * Param must be index into child process table.
798 ChildMonitorThread(LPVOID param)
800 int tidx = (int)(intptr_t)param;
801 HANDLE childProcHandle;
805 /* retrieve handle for child process from process table and duplicate */
807 (void)pthread_mutex_lock(&procTableLock);
809 fsuccess = DuplicateHandle(GetCurrentProcess(), /* source process handle */
810 procTable[tidx].p_handle, /* source handle to dup */
811 GetCurrentProcess(), /* target process handle */
812 &childProcHandle, /* target handle (duplicate) */
813 0, /* access (ignored here) */
814 FALSE, /* not inheritable */
815 DUPLICATE_SAME_ACCESS);
817 (void)pthread_mutex_unlock(&procTableLock);
820 /* wait for child process to terminate */
822 if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
823 /* child process terminated; mark in table and signal event */
824 (void)pthread_mutex_lock(&procTableLock);
826 procTable[tidx].p_terminated = TRUE;
827 (void)GetExitCodeProcess(childProcHandle,
828 &procTable[tidx].p_status);
831 (void)pthread_mutex_unlock(&procTableLock);
833 (void)pthread_cond_broadcast(&childTermEvent);
835 /* process/raise SIGCHLD; do last in case handler never returns */
836 ProcessSignal(SIGCHLD);
840 (void)CloseHandle(childProcHandle);
843 /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
844 * fail; however, this should never happen.
852 * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
854 * Returns pid of the child process ((pid_t)-1 on failure with errno set).
856 * Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
857 * Open files are not inherited; child's stdin, stdout, and stderr
858 * are set to parent's console.
859 * If spath does not specify a filename extension ".exe" is used.
860 * If sdatap is not NULL, and sdatalen > 0, data is passed to child.
861 * The spath and sargv[] strings must not contain quote chars (").
863 * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
866 pmgt_ProcessSpawnVEB(const char *spath, char *sargv[], char *senvp[],
867 void *sdatap, size_t sdatalen)
870 char *pathbuf, *argbuf, *envbuf;
871 char pathext[_MAX_EXT];
872 STARTUPINFO startInfo;
873 PROCESS_INFORMATION procInfo;
874 HANDLE monitorHandle = NULL;
875 HANDLE bufMemHandle, bufEventHandle;
876 DWORD monitorId, createFlags;
877 BOOL passingBuffer = (sdatap != NULL && sdatalen > 0);
881 /* verify arguments */
882 if (!spath || !sargv) {
885 } else if (*spath == '\0') {
890 /* create path with .exe extension if no filename extension supplied */
891 if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */ ))) {
893 return ((pid_t) - 1);
895 strcpy(pathbuf, spath);
897 _splitpath(pathbuf, NULL, NULL, NULL, pathext);
898 if (*pathext == '\0') {
899 /* no filename extension supplied for spath; .exe is assumed */
900 strcat(pathbuf, ".exe");
903 /* create command line argument string */
904 argbuf = StringArrayToString(sargv);
909 return ((pid_t) - 1);
912 /* create environment variable block (multistring) */
914 /* use environment variables provided */
915 envbuf = StringArrayToMultiString(senvp);
921 return ((pid_t) - 1);
924 /* use default environment variables */
928 /* set process creation flags */
929 createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
931 if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
932 createFlags |= DETACHED_PROCESS;
935 /* clear start-up info; use defaults */
936 memset((void *)&startInfo, 0, sizeof(startInfo));
937 startInfo.cb = sizeof(startInfo);
939 /* perform the following as a logically atomic unit:
940 * 1) allocate a process table entry
941 * 2) spawn child process (suspended)
942 * 3) create data buffer to pass (optional)
943 * 4) initialize process table entry
944 * 5) start child watcher thread
945 * 6) resume spawned child process
948 (void)pthread_mutex_lock(&procTableLock);
950 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
951 if (procTable[tidx].p_handle == NULL
952 && procTable[tidx].p_reserved == FALSE) {
953 procTable[tidx].p_reserved = TRUE;
957 (void)pthread_mutex_unlock(&procTableLock);
959 if (tidx >= PMGT_CHILD_MAX) {
960 /* no space left in process table */
969 fsuccess = CreateProcess(pathbuf, /* executable path */
970 argbuf, /* command line argument string */
971 NULL, /* default process security attr */
972 NULL, /* default thread security attr */
973 FALSE, /* do NOT inherit handles */
974 createFlags, /* creation control flags */
975 envbuf, /* environment variable block */
976 NULL, /* current directory is that of parent */
977 &startInfo, /* startup info block */
980 lasterror = GetLastError();
986 /* failed to spawn process */
987 errno = nterr_nt2unix(lasterror, ENOENT);
989 (void)pthread_mutex_lock(&procTableLock);
990 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
991 (void)pthread_mutex_unlock(&procTableLock);
997 /* create named data buffer and read event for child */
999 CreateChildDataBuffer(procInfo.dwProcessId, sdatap, sdatalen,
1000 &bufMemHandle, &bufEventHandle);
1002 (void)pthread_mutex_lock(&procTableLock);
1003 procTable[tidx].p_reserved = FALSE; /* mark entry not reserved */
1004 (void)pthread_mutex_unlock(&procTableLock);
1006 (void)TerminateProcess(procInfo.hProcess,
1007 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1008 (void)CloseHandle(procInfo.hThread);
1009 (void)CloseHandle(procInfo.hProcess);
1016 (void)pthread_mutex_lock(&procTableLock);
1018 procTable[tidx].p_handle = procInfo.hProcess;
1019 procTable[tidx].p_id = procInfo.dwProcessId;
1020 procTable[tidx].p_terminated = FALSE;
1024 /* Note: must hold procTableLock during monitor thread creation so
1025 * that if creation fails we can clean up process table before another
1026 * thread has a chance to see this procTable entry. Continue to hold
1027 * procTableLock while resuming child process, since the procTable
1028 * entry contains a copy of the child process handle which we might use.
1030 monitorHandle = CreateThread(NULL, /* default security attr. */
1031 0, /* default stack size */
1032 ChildMonitorThread, (LPVOID)(intptr_t) tidx, /* thread argument */
1033 0, /* creation flags */
1034 &monitorId); /* thread id */
1036 if (monitorHandle == NULL) {
1037 /* failed to start child monitor thread */
1038 procTable[tidx].p_handle = NULL; /* invalidate table entry */
1039 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
1042 (void)pthread_mutex_unlock(&procTableLock);
1044 (void)TerminateProcess(procInfo.hProcess,
1045 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1046 (void)CloseHandle(procInfo.hThread);
1047 (void)CloseHandle(procInfo.hProcess);
1049 if (passingBuffer) {
1050 (void)CloseHandle(bufMemHandle);
1051 (void)CloseHandle(bufEventHandle);
1058 /* Resume child process, which was created suspended to implement spawn
1059 * atomically. If resumption fails, which it never should, terminate
1060 * the child process with a status of SIGKILL. Spawn still succeeds and
1061 * the net result is the same as if the child process received a spurious
1062 * SIGKILL signal; the child monitor thread will then handle this.
1064 if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1065 (void)TerminateProcess(procInfo.hProcess,
1066 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1068 if (passingBuffer) {
1069 /* child will never read data buffer */
1070 (void)SetEvent(bufEventHandle);
1074 (void)pthread_mutex_unlock(&procTableLock);
1076 (void)CloseHandle(procInfo.hThread);
1077 (void)CloseHandle(monitorHandle);
1079 /* After spawn returns, signals can not be sent to the new child process
1080 * until that child initializes its signal-receiving mechanism (assuming
1081 * the child is linked with this library). Shorten (but sadly don't
1082 * eliminate) this window of opportunity for failure by yielding this
1083 * thread's time slice.
1085 (void)SwitchToThread();
1087 /* If passing a data buffer to child, wait until child reads buffer
1088 * before closing handles and thus freeing resources; if don't wait
1089 * then parent can not safely exit immediately after returning from
1090 * this call (hence why wait is not done in a background thread).
1092 if (passingBuffer) {
1093 WaitForSingleObject(bufEventHandle, 10000);
1094 /* note: if wait times out, child may not get to read buffer */
1095 (void)CloseHandle(bufMemHandle);
1096 (void)CloseHandle(bufEventHandle);
1099 return (pid_t) procInfo.dwProcessId;
1105 * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1106 * for child to terminate (Unix waitpid() semantics).
1108 * Note: does not support waiting for process in group (i.e., pid
1109 * equals (pid_t)0 or pid is less than (pid_t)-1.
1112 pmgt_ProcessWaitPid(pid_t pid, int *statusP, int options)
1116 BOOL statusFound = FALSE;
1119 /* validate arguments */
1120 if (pid < (pid_t) - 1 || pid == (pid_t) 0) {
1125 /* determine how long caller is willing to wait for child */
1127 waitTime = (options & WNOHANG) ? 0 : INFINITE;
1129 /* get child status */
1131 (void)pthread_mutex_lock(&procTableLock);
1134 BOOL waitForChild = FALSE;
1136 if (procEntryCount == 0) {
1137 /* no child processes */
1141 /* determine if status is available for specified child id */
1143 if (pid == (pid_t) - 1) {
1144 /* CASE 1: pid matches any child id */
1146 if (procTermCount == 0) {
1147 /* status not available for any child ... */
1148 if (waitTime == 0) {
1149 /* ... and caller is not willing to wait */
1152 /* ... but caller is willing to wait */
1153 waitForChild = TRUE;
1156 /* status available for some child; locate table entry */
1157 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1158 if (procTable[tidx].p_handle != NULL
1159 && procTable[tidx].p_terminated == TRUE) {
1166 /* should never happen; indicates a bug */
1167 errno = EINTR; /* plausible lie for failure */
1173 /* CASE 2: pid must match a specific child id */
1175 /* locate table entry */
1176 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1177 if (procTable[tidx].p_handle != NULL
1178 && procTable[tidx].p_id == (DWORD) pid) {
1183 if (tidx >= PMGT_CHILD_MAX) {
1184 /* pid does not match any child id */
1187 } else if (procTable[tidx].p_terminated == FALSE) {
1188 /* status not available for specified child ... */
1189 if (waitTime == 0) {
1190 /* ... and caller is not willing to wait */
1193 /* ... but caller is willing to wait */
1194 waitForChild = TRUE;
1197 /* status is available for specified child */
1204 (void)pthread_cond_wait(&childTermEvent, &procTableLock);
1211 /* child status available */
1213 *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1215 rc = (pid_t) procTable[tidx].p_id;
1217 /* clean up process table */
1218 (void)CloseHandle(procTable[tidx].p_handle);
1219 procTable[tidx].p_handle = NULL;
1220 procTable[tidx].p_reserved = FALSE;
1226 (void)pthread_mutex_unlock(&procTableLock);
1235 /* ----------------- General ---------------- */
1240 * PmgtLibraryInitialize() -- Initialize process management library.
1243 PmgtLibraryInitialize(void)
1246 HANDLE sigPipeHandle;
1247 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1248 HANDLE sigListenerHandle;
1249 DWORD sigListenerId;
1251 /* initialize mutex locks and condition variables */
1253 if ((rc = pthread_mutex_init(&signalTableLock, NULL))
1254 || (rc = pthread_mutex_init(&signalBlockLock, NULL))
1255 || (rc = pthread_mutex_init(&procTableLock, NULL))
1256 || (rc = pthread_cond_init(&childTermEvent, NULL))) {
1261 /* initialize signal disposition table */
1263 for (i = 0; i < NSIG; i++) {
1264 if (SignalIsDefined(i)) {
1265 /* initialize to default action for defined signals */
1266 signalTable[i].action.sa_handler = SIG_DFL;
1267 sigemptyset(&signalTable[i].action.sa_mask);
1268 signalTable[i].action.sa_flags = 0;
1270 /* initialize to ignore for undefined signals */
1271 signalTable[i].action.sa_handler = SIG_IGN;
1275 /* initialize child process table */
1277 for (i = 0; i < PMGT_CHILD_MAX; i++) {
1278 procTable[i].p_handle = NULL;
1279 procTable[i].p_reserved = FALSE;
1284 /* retrieve data buffer passed from parent in spawn, if any */
1286 if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1287 pmgt_spawnData = NULL;
1288 pmgt_spawnDataLen = 0;
1291 /* create named pipe for delivering signals to this process */
1293 sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX,
1294 (int)GetCurrentProcessId());
1296 sigPipeHandle = CreateNamedPipe(sigPipeName, /* pipe for this process */
1297 PIPE_ACCESS_DUPLEX | /* full duplex pipe */
1298 WRITE_DAC, /* DACL write access */
1299 PIPE_TYPE_MESSAGE | /* message type pipe */
1300 PIPE_READMODE_MESSAGE | /* message read-mode */
1301 PIPE_WAIT, /* blocking mode */
1302 1, /* max of 1 pipe instance */
1303 64, /* output buffer size (advisory) */
1304 64, /* input buffer size (advisory) */
1305 1000, /* 1 sec default client timeout */
1306 NULL); /* default security attr. */
1308 if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1309 /* failed to create signal pipe */
1310 errno = nterr_nt2unix(GetLastError(), EIO);
1314 /* add entry to signal pipe ACL granting local Administrators R/W access */
1316 (void)ObjectDaclEntryAdd(sigPipeHandle, SE_KERNEL_OBJECT,
1317 LocalAdministratorsGroup,
1318 GENERIC_READ | GENERIC_WRITE, GRANT_ACCESS,
1321 /* start signal pipe listener thread */
1323 sigListenerHandle = CreateThread(NULL, /* default security attr. */
1324 0, /* default stack size */
1325 RemoteSignalListenerThread, (LPVOID) sigPipeHandle, /* thread argument */
1326 0, /* creation flags */
1327 &sigListenerId); /* thread id */
1329 if (sigListenerHandle != NULL) {
1330 /* listener thread started; bump priority */
1331 (void)SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1332 (void)CloseHandle(sigListenerHandle);
1334 /* failed to start listener thread */
1336 (void)CloseHandle(sigPipeHandle);
1340 /* redirect native NT signals into this process management library */
1342 if (pmgt_RedirectNativeSignals()) {
1343 /* errno set by called function */
1352 * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1353 * function in the MSVC runtime DLL (msvcrt.dll).
1355 * Note: the system serializes calls to this function.
1358 DllMain(HINSTANCE dllInstHandle, /* instance handle for this DLL module */
1359 DWORD reason, /* reason function is being called */
1361 { /* reserved for future use */
1363 case DLL_PROCESS_ATTACH:
1364 /* library is being attached to a process */
1365 if (PmgtLibraryInitialize()) {
1366 /* failed to initialize library */
1370 /* disable thread attach/detach notifications */
1371 (void)DisableThreadLibraryCalls(dllInstHandle);
1373 case DLL_PROCESS_DETACH:
1374 pmgt_RestoreNativeSignals();