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>
25 #include <afs/errmap_nt.h>
26 #include <afs/secutil_nt.h>
29 #include "pmgtprivate.h"
33 /* Signal disposition table and associated definitions and locks */
36 struct sigaction action; /* signal action information */
39 static sigtable_entry_t signalTable[NSIG]; /* signal table; slot 0 unused */
40 static pthread_mutex_t signalTableLock; /* lock protects signalTable */
42 /* Global signal block lock; all signal handlers are serialized for now */
43 static pthread_mutex_t signalBlockLock;
45 /* Named pipe prefix for sending signals */
46 #define PMGT_SIGNAL_PIPE_PREFIX "\\\\.\\pipe\\TransarcAfsSignalPipe"
48 /* Macro to test process exit status for an uncaught exception */
49 #define PMGT_IS_EXPSTATUS(status) (((status) & 0xF0000000) == 0xC0000000)
53 /* Child process table and associated definitions and locks */
56 HANDLE p_handle; /* process handle (NULL if table entry not valid) */
57 BOOL p_reserved; /* table entry is reserved for spawn */
58 DWORD p_id; /* process id */
59 BOOL p_terminated; /* process terminated; status available/valid */
60 DWORD p_status; /* status of terminated process */
63 /* Max number of active child processes supported */
64 #define PMGT_CHILD_MAX 100
66 static proctable_entry_t procTable[PMGT_CHILD_MAX]; /* child process table */
67 static int procEntryCount; /* count of valid entries in procTable */
68 static int procTermCount; /* count of terminated entries in procTable */
70 /* lock protects procTable, procEntryCount, and procTermCount */
71 static pthread_mutex_t procTableLock;
73 /* Named shared memory prefix for passing a data buffer to a child process */
74 #define PMGT_DATA_MEM_PREFIX "TransarcAfsSpawnDataMemory"
76 /* Named event prefix for indicating that a data buffer has been read */
77 #define PMGT_DATA_EVENT_PREFIX "TransarcAfsSpawnDataEvent"
79 /* event signals termination of a child process */
80 static pthread_cond_t childTermEvent;
83 /* Exported data values */
85 void *pmgt_spawnData = NULL;
86 size_t pmgt_spawnDataLen = 0;
89 /* General definitions */
91 #define DWORD_OF_ONES ((DWORD)0xFFFFFFFF) /* a common Win32 failure code */
98 /* ----------------- Signals ---------------- */
102 * SignalIsDefined() -- Determine if an integer value corresponds to a
103 * signal value defined for this platform.
106 SignalIsDefined(int signo)
110 if (signo >= 1 && signo <= (NSIG - 1)) {
111 /* value is in valid range; check specifics */
135 * DefaultActionHandler() -- Execute the default action for the given signal.
138 DefaultActionHandler(int signo)
146 /* default action is "exit" */
147 ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
153 /* default action is "core" */
154 /* Best we can do is to raise an exception that can be caught by
155 * Dr. Watson, which can in turn generate a crash dump file.
156 * The default exception handler will call ExitProcess() with
157 * our application-specific exception code.
159 RaiseException((DWORD) PMGT_SIGSTATUS_ENCODE(signo),
160 EXCEPTION_NONCONTINUABLE, 0, NULL);
165 /* default action is "ignore" */
168 /* default action is "stop" */
169 /* No good way to implement this from inside a process so ignore */
172 /* no default action for specified signal value; just ignore */
179 * ProcessSignal() -- Execute the specified or default handler for the given
180 * signal; reset the signal's disposition to SIG_DFL if necessary.
181 * If the signal's disposition is SIG_IGN then no processing takes place.
183 * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
186 ProcessSignal(int signo)
188 struct sigaction sigEntry;
190 if (signo != SIGKILL) {
191 /* serialize signals, but never block processing of SIGKILL */
192 (void)pthread_mutex_lock(&signalBlockLock);
195 /* fetch disposition of signo, updating it if necessary */
197 (void)pthread_mutex_lock(&signalTableLock);
198 sigEntry = signalTable[signo].action;
200 if ((sigEntry.sa_handler != SIG_IGN) && (sigEntry.sa_flags & SA_RESETHAND)
201 && (signo != SIGILL)) {
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)(intptr_t)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;
254 HMODULE hLib = LoadLibrary("AFSPROCMGMT.DLL");
257 /* wait for pipe client to connect */
259 if ((ConnectNamedPipe(sigPipeHandle, NULL))
260 || (GetLastError() == ERROR_PIPE_CONNECTED)) {
261 /* client connected; read signal value */
266 (sigPipeHandle, &signo, sizeof(signo), &bytesXfered, NULL))
267 && (bytesXfered == sizeof(signo))) {
268 HANDLE sigThreadHandle;
271 /* ACK signal to release sender */
272 (void)WriteFile(sigPipeHandle, &signo, sizeof(signo),
275 /* spawn thread to process signal; we do this so that
276 * we can always process a SIGKILL even if a signal handler
277 * invoked earlier fails to return (blocked/spinning).
279 sigThreadHandle = CreateThread(NULL, /* default security attr. */
280 0, /* default stack size */
281 RemoteSignalThread, (LPVOID) (intptr_t)signo, /* thread argument */
282 0, /* creation flags */
283 &sigThreadId); /* thread id */
285 if (sigThreadHandle != NULL) {
286 (void)CloseHandle(sigThreadHandle);
289 /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
292 /* connect failed; this should never happen */
293 Sleep(2000); /* sleep 2 seconds to avoid tight loop */
296 (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, const struct sigaction *actionP,
314 struct sigaction *old_actionP)
316 /* validate arguments */
318 if (!SignalIsDefined(signo) || signo == SIGKILL) {
319 /* invalid signal value or signal can't be caught/ignored */
324 if (actionP && actionP->sa_handler == SIG_ERR) {
325 /* invalid signal disposition */
330 /* fetch and/or set disposition of signo */
332 (void)pthread_mutex_lock(&signalTableLock);
335 *old_actionP = signalTable[signo].action;
339 signalTable[signo].action = *actionP;
342 (void)pthread_mutex_unlock(&signalTableLock);
349 * pmgt_SignalSet() -- Specify the disposition for a given signal
350 * value (Unix signal() semantics).
352 void (__cdecl * pmgt_SignalSet(int signo, void (__cdecl * dispP) (int))) (int) {
353 struct sigaction newAction, oldAction;
355 /* construct action to request Unix signal() semantics */
357 newAction.sa_handler = dispP;
358 sigemptyset(&newAction.sa_mask);
359 newAction.sa_flags = SA_RESETHAND;
361 if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
362 /* successfully set new signal action */
363 return oldAction.sa_handler;
365 /* failed to set signal action; errno will have been set */
372 * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
376 pmgt_SignalRaiseLocal(int signo)
380 /* Process signal directly in the context of the calling thread.
381 * This is the same as if the signal had been raised in this process
382 * and this thread chosen to execute the handler.
385 if (SignalIsDefined(signo)) {
387 ProcessSignal(signo);
388 } else if (signo != 0) {
389 /* invalid signal value */
398 * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
399 * the signal is specified by name (C raise() semantics).
401 * Upon successful completion, *libSigno is set to the process management
402 * library's constant value for signame.
404 * Note: exists to implement the native-signal redirector (redirect_nt.c),
405 * which can't include procmgmt.h and hence can't get the SIG* decls.
408 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
413 if (!strcmp(signame, "SIGHUP")) {
415 } else if (!strcmp(signame, "SIGINT")) {
417 } else if (!strcmp(signame, "SIGQUIT")) {
419 } else if (!strcmp(signame, "SIGILL")) {
421 } else if (!strcmp(signame, "SIGABRT")) {
423 } else if (!strcmp(signame, "SIGFPE")) {
425 } else if (!strcmp(signame, "SIGKILL")) {
427 } else if (!strcmp(signame, "SIGSEGV")) {
429 } else if (!strcmp(signame, "SIGTERM")) {
431 } else if (!strcmp(signame, "SIGUSR1")) {
433 } else if (!strcmp(signame, "SIGUSR2")) {
435 } else if (!strcmp(signame, "SIGCLD")) {
437 } else if (!strcmp(signame, "SIGCHLD")) {
439 } else if (!strcmp(signame, "SIGTSTP")) {
442 /* unknown signal name */
449 rc = pmgt_SignalRaiseLocal(signo);
456 * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
459 * Note: only supports sending signal to a specific (single) process.
462 pmgt_SignalRaiseRemote(pid_t pid, int signo)
465 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
470 /* validate arguments */
472 if ((pid <= (pid_t) 0) || (!SignalIsDefined(signo) && signo != 0)) {
473 /* invalid pid or signo */
478 /* optimize for the "this process" case */
480 if (pid == (pid_t) GetCurrentProcessId()) {
481 return pmgt_SignalRaiseLocal(signo);
484 /* send signal to process via named pipe */
486 sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
488 fsuccess = CallNamedPipe(sigPipeName, /* process pid's signal pipe */
489 &signo, /* data written to pipe */
490 sizeof(signo), /* size of data to write */
491 &signoACK, /* data read from pipe */
492 sizeof(signoACK), /* size of data read buffer */
493 &ackBytesRead, /* number of bytes actually read */
494 5 * 1000); /* 5 second timeout */
497 /* failed to send signal via named pipe */
500 if (signo == SIGKILL) {
501 /* could be a non-AFS process, which might still be kill-able */
505 OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid)) {
507 (procHandle, PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
508 /* successfully killed process */
511 errno = nterr_nt2unix(GetLastError(), EPERM);
513 (void)CloseHandle(procHandle);
515 if (GetLastError() == ERROR_INVALID_PARAMETER) {
517 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
520 errno = nterr_nt2unix(GetLastError(), EPERM);
524 /* couldn't open pipe so can't send (non-SIGKILL) signal */
525 errno = nterr_nt2unix(GetLastError(), EPERM);
535 /* ----------------- Processes ---------------- */
539 * StringArrayToString() -- convert a null-terminated array of strings,
540 * such as argv, into a single string of space-separated elements
541 * with each element quoted (in case it contains space characters
542 * or is of zero length).
545 StringArrayToString(char *strArray[])
551 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
552 /* sum all string lengths */
553 byteCount += (int)strlen(strArray[strCount]);
556 /* put all strings into buffer; guarantee buffer is at least one char */
557 buffer = (char *)malloc(byteCount + (strCount * 3) /* quotes+space */ +1);
558 if (buffer != NULL) {
563 for (i = 0; i < strCount; i++) {
564 char *bufp = buffer + strlen(buffer);
566 if (i == strCount - 1) {
567 /* last string; no trailing space */
568 sprintf(bufp, "\"%s\"", strArray[i]);
570 sprintf(bufp, "\"%s\" ", strArray[i]);
580 * StringArrayToMultiString() -- convert a null-terminated array of strings,
581 * such as envp, into a multistring.
584 StringArrayToMultiString(char *strArray[])
590 for (strCount = 0; strArray[strCount] != NULL; strCount++) {
591 /* sum all string lengths */
592 byteCount += strlen(strArray[strCount]);
595 /* put all strings into buffer; guarantee buffer is at least two chars */
596 buffer = (char *)malloc(byteCount + strCount + 2);
597 if (buffer != NULL) {
598 if (byteCount == 0) {
605 for (i = 0; i < strCount; i++) {
606 int strLen = strlen(strArray[i]);
609 /* can not embed zero length string in a multistring */
610 strcpy(bufp, strArray[i]);
614 bufp = '\0'; /* terminate multistring */
624 * ComputeWaitStatus() -- Compute an appropriate wait status value from
625 * a given process termination (exit) code.
628 ComputeWaitStatus(DWORD exitStatus)
632 if (PMGT_IS_SIGSTATUS(exitStatus)) {
633 /* child terminated due to an unhandled signal */
634 int signo = PMGT_SIGSTATUS_DECODE(exitStatus);
635 waitStatus = WSIGNALED_ENCODE(signo);
636 } else if (PMGT_IS_EXPSTATUS(exitStatus)) {
637 /* child terminated due to an uncaught exception */
640 switch (exitStatus) {
641 case EXCEPTION_FLT_DENORMAL_OPERAND:
642 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
643 case EXCEPTION_FLT_INEXACT_RESULT:
644 case EXCEPTION_FLT_INVALID_OPERATION:
645 case EXCEPTION_FLT_OVERFLOW:
646 case EXCEPTION_FLT_STACK_CHECK:
647 case EXCEPTION_FLT_UNDERFLOW:
648 case EXCEPTION_INT_DIVIDE_BY_ZERO:
649 case EXCEPTION_INT_OVERFLOW:
652 case EXCEPTION_PRIV_INSTRUCTION:
653 case EXCEPTION_ILLEGAL_INSTRUCTION:
663 waitStatus = WSIGNALED_ENCODE(signo);
665 /* child terminated normally */
666 waitStatus = WEXITED_ENCODE(exitStatus);
675 * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
676 * a child process, along with a corresponding buffer read event.
678 * ASSUMPTIONS: child process is linked with this process management library;
679 * otherwise no data transfer will take place.
682 CreateChildDataBuffer(DWORD pid, /* child pid */
683 void *datap, /* data to place in buffer */
684 size_t dataLen, /* size of data in bytes */
685 HANDLE * bufMemHandlep, /* buffer memory handle */
686 HANDLE * bufEventHandlep)
687 { /* buffer read event handle */
688 BOOL fsuccess = FALSE;
689 DWORD bufMemSize = dataLen + (DWORD)sizeof(size_t);
690 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
691 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
693 sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
694 sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
696 /* Create and initialize named shared memory and named event */
698 *bufMemHandlep = CreateFileMapping(INVALID_HANDLE_VALUE, /* page-file backed */
699 NULL, PAGE_READWRITE, 0, bufMemSize,
702 if (*bufMemHandlep != NULL) {
706 MapViewOfFile(*bufMemHandlep, FILE_MAP_WRITE, 0, 0, bufMemSize);
708 if (bufMemp != NULL) {
709 /* copy data into shared memory, prefixed with data size */
710 size_t *memp = (size_t *) bufMemp;
713 memcpy((void *)memp, datap, dataLen);
715 if (UnmapViewOfFile(bufMemp)) {
716 /* create buffer read event */
718 CreateEvent(NULL, FALSE /* manual reset */ ,
719 FALSE /* initial state */ ,
721 if (*bufEventHandlep != NULL) {
728 (void)CloseHandle(*bufMemHandlep);
733 *bufMemHandlep = *bufEventHandlep = NULL;
741 * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
742 * if any, and place in allocated storage.
745 ReadChildDataBuffer(void **datap, /* allocated data buffer */
747 { /* size of data buffer returned */
748 BOOL fsuccess = FALSE;
749 char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
750 char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
751 HANDLE bufMemHandle, bufEventHandle;
753 sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX,
754 (int)GetCurrentProcessId());
755 sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX,
756 (int)GetCurrentProcessId());
758 /* Attempt to open named event and named shared memory */
760 bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
762 if (bufEventHandle != NULL) {
763 bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
765 if (bufMemHandle != NULL) {
768 bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
770 if (bufMemp != NULL) {
771 /* read data size and data from shared memory */
772 size_t *memp = (size_t *) bufMemp;
775 *datap = (void *)malloc(*dataLen);
777 if (*datap != NULL) {
778 memcpy(*datap, (void *)memp, *dataLen);
781 (void)UnmapViewOfFile(bufMemp);
784 (void)CloseHandle(bufMemHandle);
787 (void)SetEvent(bufEventHandle);
788 (void)CloseHandle(bufEventHandle);
801 * ChildMonitorThread() -- Thread spawned to monitor status of child process.
803 * Param must be index into child process table.
806 ChildMonitorThread(LPVOID param)
808 int tidx = (int)(intptr_t)param;
809 HANDLE childProcHandle;
813 /* retrieve handle for child process from process table and duplicate */
815 (void)pthread_mutex_lock(&procTableLock);
817 fsuccess = DuplicateHandle(GetCurrentProcess(), /* source process handle */
818 procTable[tidx].p_handle, /* source handle to dup */
819 GetCurrentProcess(), /* target process handle */
820 &childProcHandle, /* target handle (duplicate) */
821 0, /* access (ignored here) */
822 FALSE, /* not inheritable */
823 DUPLICATE_SAME_ACCESS);
825 (void)pthread_mutex_unlock(&procTableLock);
828 /* wait for child process to terminate */
830 if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
831 /* child process terminated; mark in table and signal event */
832 (void)pthread_mutex_lock(&procTableLock);
834 procTable[tidx].p_terminated = TRUE;
835 (void)GetExitCodeProcess(childProcHandle,
836 &procTable[tidx].p_status);
839 (void)pthread_mutex_unlock(&procTableLock);
841 (void)pthread_cond_broadcast(&childTermEvent);
843 /* process/raise SIGCHLD; do last in case handler never returns */
844 ProcessSignal(SIGCHLD);
848 (void)CloseHandle(childProcHandle);
851 /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
852 * fail; however, this should never happen.
860 * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
862 * Returns pid of the child process ((pid_t)-1 on failure with errno set).
864 * Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
865 * Open files are not inherited; child's stdin, stdout, and stderr
866 * are set to parent's console.
867 * If spath does not specify a filename extension ".exe" is used.
868 * If sdatap is not NULL, and sdatalen > 0, data is passed to child.
869 * The spath and sargv[] strings must not contain quote chars (").
871 * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
874 pmgt_ProcessSpawnVEB(const char *spath, char *sargv[], char *senvp[],
875 void *sdatap, size_t sdatalen)
878 char *pathbuf, *argbuf, *envbuf;
879 char pathext[_MAX_EXT];
880 STARTUPINFO startInfo;
881 PROCESS_INFORMATION procInfo;
882 HANDLE monitorHandle = NULL;
883 HANDLE bufMemHandle, bufEventHandle;
884 DWORD monitorId, createFlags;
885 BOOL passingBuffer = (sdatap != NULL && sdatalen > 0);
888 /* verify arguments */
889 if (!spath || !sargv) {
892 } else if (*spath == '\0') {
897 /* create path with .exe extension if no filename extension supplied */
898 if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */ ))) {
900 return ((pid_t) - 1);
902 strcpy(pathbuf, spath);
904 _splitpath(pathbuf, NULL, NULL, NULL, pathext);
905 if (*pathext == '\0') {
906 /* no filename extension supplied for spath; .exe is assumed */
907 strcat(pathbuf, ".exe");
910 /* create command line argument string */
911 argbuf = StringArrayToString(sargv);
916 return ((pid_t) - 1);
919 /* create environment variable block (multistring) */
921 /* use environment variables provided */
922 envbuf = StringArrayToMultiString(senvp);
928 return ((pid_t) - 1);
931 /* use default environment variables */
935 /* set process creation flags */
936 createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
938 if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
939 createFlags |= DETACHED_PROCESS;
942 /* clear start-up info; use defaults */
943 memset((void *)&startInfo, 0, sizeof(startInfo));
944 startInfo.cb = sizeof(startInfo);
946 /* perform the following as a logically atomic unit:
947 * 1) allocate a process table entry
948 * 2) spawn child process (suspended)
949 * 3) create data buffer to pass (optional)
950 * 4) initialize process table entry
951 * 5) start child watcher thread
952 * 6) resume spawned child process
955 (void)pthread_mutex_lock(&procTableLock);
957 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
958 if (procTable[tidx].p_handle == NULL
959 && procTable[tidx].p_reserved == FALSE) {
960 procTable[tidx].p_reserved = TRUE;
964 (void)pthread_mutex_unlock(&procTableLock);
966 if (tidx >= PMGT_CHILD_MAX) {
967 /* no space left in process table */
976 fsuccess = CreateProcess(pathbuf, /* executable path */
977 argbuf, /* command line argument string */
978 NULL, /* default process security attr */
979 NULL, /* default thread security attr */
980 FALSE, /* do NOT inherit handles */
981 createFlags, /* creation control flags */
982 envbuf, /* environment variable block */
983 NULL, /* current directory is that of parent */
984 &startInfo, /* startup info block */
992 /* failed to spawn process */
993 errno = nterr_nt2unix(GetLastError(), ENOENT);
995 (void)pthread_mutex_lock(&procTableLock);
996 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
997 (void)pthread_mutex_unlock(&procTableLock);
1002 if (passingBuffer) {
1003 /* create named data buffer and read event for child */
1005 CreateChildDataBuffer(procInfo.dwProcessId, sdatap, sdatalen,
1006 &bufMemHandle, &bufEventHandle);
1008 (void)pthread_mutex_lock(&procTableLock);
1009 procTable[tidx].p_reserved = FALSE; /* mark entry not reserved */
1010 (void)pthread_mutex_unlock(&procTableLock);
1012 (void)TerminateProcess(procInfo.hProcess,
1013 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1014 (void)CloseHandle(procInfo.hThread);
1015 (void)CloseHandle(procInfo.hProcess);
1022 (void)pthread_mutex_lock(&procTableLock);
1024 procTable[tidx].p_handle = procInfo.hProcess;
1025 procTable[tidx].p_id = procInfo.dwProcessId;
1026 procTable[tidx].p_terminated = FALSE;
1030 /* Note: must hold procTableLock during monitor thread creation so
1031 * that if creation fails we can clean up process table before another
1032 * thread has a chance to see this procTable entry. Continue to hold
1033 * procTableLock while resuming child process, since the procTable
1034 * entry contains a copy of the child process handle which we might use.
1036 monitorHandle = CreateThread(NULL, /* default security attr. */
1037 0, /* default stack size */
1038 ChildMonitorThread, (LPVOID)(intptr_t) tidx, /* thread argument */
1039 0, /* creation flags */
1040 &monitorId); /* thread id */
1042 if (monitorHandle == NULL) {
1043 /* failed to start child monitor thread */
1044 procTable[tidx].p_handle = NULL; /* invalidate table entry */
1045 procTable[tidx].p_reserved = FALSE; /* mark entry as not reserved */
1048 (void)pthread_mutex_unlock(&procTableLock);
1050 (void)TerminateProcess(procInfo.hProcess,
1051 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1052 (void)CloseHandle(procInfo.hThread);
1053 (void)CloseHandle(procInfo.hProcess);
1055 if (passingBuffer) {
1056 (void)CloseHandle(bufMemHandle);
1057 (void)CloseHandle(bufEventHandle);
1064 /* Resume child process, which was created suspended to implement spawn
1065 * atomically. If resumption fails, which it never should, terminate
1066 * the child process with a status of SIGKILL. Spawn still succeeds and
1067 * the net result is the same as if the child process received a spurious
1068 * SIGKILL signal; the child monitor thread will then handle this.
1070 if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1071 (void)TerminateProcess(procInfo.hProcess,
1072 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1074 if (passingBuffer) {
1075 /* child will never read data buffer */
1076 (void)SetEvent(bufEventHandle);
1080 (void)pthread_mutex_unlock(&procTableLock);
1082 (void)CloseHandle(procInfo.hThread);
1083 (void)CloseHandle(monitorHandle);
1085 /* After spawn returns, signals can not be sent to the new child process
1086 * until that child initializes its signal-receiving mechanism (assuming
1087 * the child is linked with this library). Shorten (but sadly don't
1088 * eliminate) this window of opportunity for failure by yielding this
1089 * thread's time slice.
1091 (void)SwitchToThread();
1093 /* If passing a data buffer to child, wait until child reads buffer
1094 * before closing handles and thus freeing resources; if don't wait
1095 * then parent can not safely exit immediately after returning from
1096 * this call (hence why wait is not done in a background thread).
1098 if (passingBuffer) {
1099 WaitForSingleObject(bufEventHandle, 10000);
1100 /* note: if wait times out, child may not get to read buffer */
1101 (void)CloseHandle(bufMemHandle);
1102 (void)CloseHandle(bufEventHandle);
1105 return (pid_t) procInfo.dwProcessId;
1111 * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1112 * for child to terminate (Unix waitpid() semantics).
1114 * Note: does not support waiting for process in group (i.e., pid
1115 * equals (pid_t)0 or pid is less than (pid_t)-1.
1118 pmgt_ProcessWaitPid(pid_t pid, int *statusP, int options)
1122 BOOL statusFound = FALSE;
1125 /* validate arguments */
1126 if (pid < (pid_t) - 1 || pid == (pid_t) 0) {
1131 /* determine how long caller is willing to wait for child */
1133 waitTime = (options & WNOHANG) ? 0 : INFINITE;
1135 /* get child status */
1137 (void)pthread_mutex_lock(&procTableLock);
1140 BOOL waitForChild = FALSE;
1142 if (procEntryCount == 0) {
1143 /* no child processes */
1147 /* determine if status is available for specified child id */
1149 if (pid == (pid_t) - 1) {
1150 /* CASE 1: pid matches any child id */
1152 if (procTermCount == 0) {
1153 /* status not available for any child ... */
1154 if (waitTime == 0) {
1155 /* ... and caller is not willing to wait */
1158 /* ... but caller is willing to wait */
1159 waitForChild = TRUE;
1162 /* status available for some child; locate table entry */
1163 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1164 if (procTable[tidx].p_handle != NULL
1165 && procTable[tidx].p_terminated == TRUE) {
1172 /* should never happen; indicates a bug */
1173 errno = EINTR; /* plausible lie for failure */
1179 /* CASE 2: pid must match a specific child id */
1181 /* locate table entry */
1182 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1183 if (procTable[tidx].p_handle != NULL
1184 && procTable[tidx].p_id == (DWORD) pid) {
1189 if (tidx >= PMGT_CHILD_MAX) {
1190 /* pid does not match any child id */
1193 } else if (procTable[tidx].p_terminated == FALSE) {
1194 /* status not available for specified child ... */
1195 if (waitTime == 0) {
1196 /* ... and caller is not willing to wait */
1199 /* ... but caller is willing to wait */
1200 waitForChild = TRUE;
1203 /* status is available for specified child */
1210 (void)pthread_cond_wait(&childTermEvent, &procTableLock);
1217 /* child status available */
1219 *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1221 rc = (pid_t) procTable[tidx].p_id;
1223 /* clean up process table */
1224 (void)CloseHandle(procTable[tidx].p_handle);
1225 procTable[tidx].p_handle = NULL;
1226 procTable[tidx].p_reserved = FALSE;
1232 (void)pthread_mutex_unlock(&procTableLock);
1241 /* ----------------- General ---------------- */
1246 * PmgtLibraryInitialize() -- Initialize process management library.
1249 PmgtLibraryInitialize(void)
1252 HANDLE sigPipeHandle;
1253 char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1254 HANDLE sigListenerHandle;
1255 DWORD sigListenerId;
1257 /* initialize mutex locks and condition variables */
1259 if ((rc = pthread_mutex_init(&signalTableLock, NULL))
1260 || (rc = pthread_mutex_init(&signalBlockLock, NULL))
1261 || (rc = pthread_mutex_init(&procTableLock, NULL))
1262 || (rc = pthread_cond_init(&childTermEvent, NULL))) {
1267 /* initialize signal disposition table */
1269 for (i = 0; i < NSIG; i++) {
1270 if (SignalIsDefined(i)) {
1271 /* initialize to default action for defined signals */
1272 signalTable[i].action.sa_handler = SIG_DFL;
1273 sigemptyset(&signalTable[i].action.sa_mask);
1274 signalTable[i].action.sa_flags = 0;
1276 /* initialize to ignore for undefined signals */
1277 signalTable[i].action.sa_handler = SIG_IGN;
1281 /* initialize child process table */
1283 for (i = 0; i < PMGT_CHILD_MAX; i++) {
1284 procTable[i].p_handle = NULL;
1285 procTable[i].p_reserved = FALSE;
1290 /* retrieve data buffer passed from parent in spawn, if any */
1292 if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1293 pmgt_spawnData = NULL;
1294 pmgt_spawnDataLen = 0;
1297 /* create named pipe for delivering signals to this process */
1299 sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX,
1300 (int)GetCurrentProcessId());
1302 sigPipeHandle = CreateNamedPipe(sigPipeName, /* pipe for this process */
1303 PIPE_ACCESS_DUPLEX | /* full duplex pipe */
1304 WRITE_DAC, /* DACL write access */
1305 PIPE_TYPE_MESSAGE | /* message type pipe */
1306 PIPE_READMODE_MESSAGE | /* message read-mode */
1307 PIPE_WAIT, /* blocking mode */
1308 1, /* max of 1 pipe instance */
1309 64, /* output buffer size (advisory) */
1310 64, /* input buffer size (advisory) */
1311 1000, /* 1 sec default client timeout */
1312 NULL); /* default security attr. */
1314 if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1315 /* failed to create signal pipe */
1316 errno = nterr_nt2unix(GetLastError(), EIO);
1320 /* add entry to signal pipe ACL granting local Administrators R/W access */
1322 (void)ObjectDaclEntryAdd(sigPipeHandle, SE_KERNEL_OBJECT,
1323 LocalAdministratorsGroup,
1324 GENERIC_READ | GENERIC_WRITE, GRANT_ACCESS,
1327 /* start signal pipe listener thread */
1329 sigListenerHandle = CreateThread(NULL, /* default security attr. */
1330 0, /* default stack size */
1331 RemoteSignalListenerThread, (LPVOID) sigPipeHandle, /* thread argument */
1332 0, /* creation flags */
1333 &sigListenerId); /* thread id */
1335 if (sigListenerHandle != NULL) {
1336 /* listener thread started; bump priority */
1337 (void)SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1338 (void)CloseHandle(sigListenerHandle);
1340 /* failed to start listener thread */
1342 (void)CloseHandle(sigPipeHandle);
1346 /* redirect native NT signals into this process management library */
1348 if (pmgt_RedirectNativeSignals()) {
1349 /* errno set by called function */
1358 * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1359 * function in the MSVC runtime DLL (msvcrt.dll).
1361 * Note: the system serializes calls to this function.
1364 DllMain(HINSTANCE dllInstHandle, /* instance handle for this DLL module */
1365 DWORD reason, /* reason function is being called */
1367 { /* reserved for future use */
1369 case DLL_PROCESS_ATTACH:
1370 /* library is being attached to a process */
1371 if (PmgtLibraryInitialize()) {
1372 /* failed to initialize library */
1376 /* disable thread attach/detach notifications */
1377 (void)DisableThreadLibraryCalls(dllInstHandle);
1379 case DLL_PROCESS_DETACH:
1380 pmgt_RestoreNativeSignals();