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 */
363 * Initialize signal handling.
365 * \note The signature of this routine matches the opr softsig
366 * registration function opr_softsig_Init() used on
367 * unix. Since this process management library is
368 * initialized by DllMain when the library is loaded,
369 * this routine is currently a no op.
371 int pmgt_SignalInit(void)
378 * Register a signal a handler.
380 * This routine is a variant of the original pmgt_SignalSet
381 * which returns a status code instead of the previously registered
384 * \note The signature of this routine matches the opr softsig
385 * registration function opr_softsig_Register() used on
388 int pmgt_SignalRegister(int signo
, void (__cdecl
*handler
)(int))
392 sa
.sa_handler
= pmgt_SignalSet(signo
, handler
);
393 if (sa
.sa_handler
== SIG_ERR
) {
402 * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
406 pmgt_SignalRaiseLocal(int signo
)
410 /* Process signal directly in the context of the calling thread.
411 * This is the same as if the signal had been raised in this process
412 * and this thread chosen to execute the handler.
415 if (SignalIsDefined(signo
)) {
417 ProcessSignal(signo
);
418 } else if (signo
!= 0) {
419 /* invalid signal value */
428 * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
429 * the signal is specified by name (C raise() semantics).
431 * Upon successful completion, *libSigno is set to the process management
432 * library's constant value for signame.
434 * Note: exists to implement the native-signal redirector (redirect_nt.c),
435 * which can't include procmgmt.h and hence can't get the SIG* decls.
438 pmgt_SignalRaiseLocalByName(const char *signame
, int *libSigno
)
443 if (!strcmp(signame
, "SIGHUP")) {
445 } else if (!strcmp(signame
, "SIGINT")) {
447 } else if (!strcmp(signame
, "SIGQUIT")) {
449 } else if (!strcmp(signame
, "SIGILL")) {
451 } else if (!strcmp(signame
, "SIGABRT")) {
453 } else if (!strcmp(signame
, "SIGFPE")) {
455 } else if (!strcmp(signame
, "SIGKILL")) {
457 } else if (!strcmp(signame
, "SIGSEGV")) {
459 } else if (!strcmp(signame
, "SIGTERM")) {
461 } else if (!strcmp(signame
, "SIGUSR1")) {
463 } else if (!strcmp(signame
, "SIGUSR2")) {
465 } else if (!strcmp(signame
, "SIGCLD")) {
467 } else if (!strcmp(signame
, "SIGCHLD")) {
469 } else if (!strcmp(signame
, "SIGTSTP")) {
472 /* unknown signal name */
479 rc
= pmgt_SignalRaiseLocal(signo
);
486 * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
489 * Note: only supports sending signal to a specific (single) process.
492 pmgt_SignalRaiseRemote(pid_t pid
, int signo
)
495 char sigPipeName
[sizeof(PMGT_SIGNAL_PIPE_PREFIX
) + 20];
500 /* validate arguments */
502 if ((pid
<= (pid_t
) 0) || (!SignalIsDefined(signo
) && signo
!= 0)) {
503 /* invalid pid or signo */
508 /* optimize for the "this process" case */
510 if (pid
== (pid_t
) GetCurrentProcessId()) {
511 return pmgt_SignalRaiseLocal(signo
);
514 /* send signal to process via named pipe */
516 sprintf(sigPipeName
, "%s%d", PMGT_SIGNAL_PIPE_PREFIX
, (int)pid
);
518 fsuccess
= CallNamedPipe(sigPipeName
, /* process pid's signal pipe */
519 &signo
, /* data written to pipe */
520 sizeof(signo
), /* size of data to write */
521 &signoACK
, /* data read from pipe */
522 sizeof(signoACK
), /* size of data read buffer */
523 &ackBytesRead
, /* number of bytes actually read */
524 5 * 1000); /* 5 second timeout */
527 /* failed to send signal via named pipe */
530 if (signo
== SIGKILL
) {
531 /* could be a non-AFS process, which might still be kill-able */
535 OpenProcess(PROCESS_TERMINATE
, FALSE
, (DWORD
) pid
)) {
537 (procHandle
, PMGT_SIGSTATUS_ENCODE(SIGKILL
))) {
538 /* successfully killed process */
541 errno
= nterr_nt2unix(GetLastError(), EPERM
);
543 (void)CloseHandle(procHandle
);
545 if (GetLastError() == ERROR_INVALID_PARAMETER
) {
547 } else if (GetLastError() == ERROR_ACCESS_DENIED
) {
550 errno
= nterr_nt2unix(GetLastError(), EPERM
);
554 /* couldn't open pipe so can't send (non-SIGKILL) signal */
555 errno
= nterr_nt2unix(GetLastError(), EPERM
);
565 /* ----------------- Processes ---------------- */
569 * StringArrayToString() -- convert a null-terminated array of strings,
570 * such as argv, into a single string of space-separated elements
571 * with each element quoted (in case it contains space characters
572 * or is of zero length).
575 StringArrayToString(char *strArray
[])
581 for (strCount
= 0; strArray
[strCount
] != NULL
; strCount
++) {
582 /* sum all string lengths */
583 byteCount
+= (int)strlen(strArray
[strCount
]);
586 /* put all strings into buffer; guarantee buffer is at least one char */
587 buffer
= malloc(byteCount
+ (strCount
* 3) /* quotes+space */ +1);
588 if (buffer
!= NULL
) {
593 for (i
= 0; i
< strCount
; i
++) {
594 char *bufp
= buffer
+ strlen(buffer
);
596 if (i
== strCount
- 1) {
597 /* last string; no trailing space */
598 sprintf(bufp
, "\"%s\"", strArray
[i
]);
600 sprintf(bufp
, "\"%s\" ", strArray
[i
]);
610 * StringArrayToMultiString() -- convert a null-terminated array of strings,
611 * such as envp, into a multistring.
614 StringArrayToMultiString(char *strArray
[])
620 for (strCount
= 0; strArray
[strCount
] != NULL
; strCount
++) {
621 /* sum all string lengths */
622 byteCount
+= strlen(strArray
[strCount
]);
625 /* put all strings into buffer; guarantee buffer is at least two chars */
626 buffer
= malloc(byteCount
+ strCount
+ 2);
627 if (buffer
!= NULL
) {
628 if (byteCount
== 0) {
635 for (i
= 0; i
< strCount
; i
++) {
636 int strLen
= strlen(strArray
[i
]);
639 /* can not embed zero length string in a multistring */
640 strcpy(bufp
, strArray
[i
]);
644 *bufp
= '\0'; /* terminate multistring */
654 * ComputeWaitStatus() -- Compute an appropriate wait status value from
655 * a given process termination (exit) code.
658 ComputeWaitStatus(DWORD exitStatus
)
662 if (PMGT_IS_SIGSTATUS(exitStatus
)) {
663 /* child terminated due to an unhandled signal */
664 int signo
= PMGT_SIGSTATUS_DECODE(exitStatus
);
665 waitStatus
= WSIGNALED_ENCODE(signo
);
666 } else if (PMGT_IS_EXPSTATUS(exitStatus
)) {
667 /* child terminated due to an uncaught exception */
670 switch (exitStatus
) {
671 case EXCEPTION_FLT_DENORMAL_OPERAND
:
672 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
673 case EXCEPTION_FLT_INEXACT_RESULT
:
674 case EXCEPTION_FLT_INVALID_OPERATION
:
675 case EXCEPTION_FLT_OVERFLOW
:
676 case EXCEPTION_FLT_STACK_CHECK
:
677 case EXCEPTION_FLT_UNDERFLOW
:
678 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
679 case EXCEPTION_INT_OVERFLOW
:
682 case EXCEPTION_PRIV_INSTRUCTION
:
683 case EXCEPTION_ILLEGAL_INSTRUCTION
:
693 waitStatus
= WSIGNALED_ENCODE(signo
);
695 /* child terminated normally */
696 waitStatus
= WEXITED_ENCODE(exitStatus
);
705 * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
706 * a child process, along with a corresponding buffer read event.
708 * ASSUMPTIONS: child process is linked with this process management library;
709 * otherwise no data transfer will take place.
712 CreateChildDataBuffer(DWORD pid
, /* child pid */
713 void *datap
, /* data to place in buffer */
714 size_t dataLen
, /* size of data in bytes */
715 HANDLE
* bufMemHandlep
, /* buffer memory handle */
716 HANDLE
* bufEventHandlep
)
717 { /* buffer read event handle */
718 BOOL fsuccess
= FALSE
;
719 DWORD bufMemSize
= dataLen
+ (DWORD
)sizeof(size_t);
720 char bufMemName
[sizeof(PMGT_DATA_MEM_PREFIX
) + 20];
721 char bufEventName
[sizeof(PMGT_DATA_EVENT_PREFIX
) + 20];
723 sprintf(bufMemName
, "%s%d", PMGT_DATA_MEM_PREFIX
, (int)pid
);
724 sprintf(bufEventName
, "%s%d", PMGT_DATA_EVENT_PREFIX
, (int)pid
);
726 /* Create and initialize named shared memory and named event */
728 *bufMemHandlep
= CreateFileMapping(INVALID_HANDLE_VALUE
, /* page-file backed */
729 NULL
, PAGE_READWRITE
, 0, bufMemSize
,
732 if (*bufMemHandlep
!= NULL
) {
736 MapViewOfFile(*bufMemHandlep
, FILE_MAP_WRITE
, 0, 0, bufMemSize
);
738 if (bufMemp
!= NULL
) {
739 /* copy data into shared memory, prefixed with data size */
740 size_t *memp
= (size_t *) bufMemp
;
743 memcpy((void *)memp
, datap
, dataLen
);
745 if (UnmapViewOfFile(bufMemp
)) {
746 /* create buffer read event */
748 CreateEvent(NULL
, FALSE
/* manual reset */ ,
749 FALSE
/* initial state */ ,
751 if (*bufEventHandlep
!= NULL
) {
758 (void)CloseHandle(*bufMemHandlep
);
763 *bufMemHandlep
= *bufEventHandlep
= NULL
;
771 * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
772 * if any, and place in allocated storage.
775 ReadChildDataBuffer(void **datap
, /* allocated data buffer */
777 { /* size of data buffer returned */
778 BOOL fsuccess
= FALSE
;
779 char bufMemName
[sizeof(PMGT_DATA_MEM_PREFIX
) + 20];
780 char bufEventName
[sizeof(PMGT_DATA_EVENT_PREFIX
) + 20];
781 HANDLE bufMemHandle
, bufEventHandle
;
783 sprintf(bufMemName
, "%s%d", PMGT_DATA_MEM_PREFIX
,
784 (int)GetCurrentProcessId());
785 sprintf(bufEventName
, "%s%d", PMGT_DATA_EVENT_PREFIX
,
786 (int)GetCurrentProcessId());
788 /* Attempt to open named event and named shared memory */
790 bufEventHandle
= OpenEvent(EVENT_MODIFY_STATE
, FALSE
, bufEventName
);
792 if (bufEventHandle
!= NULL
) {
793 bufMemHandle
= OpenFileMapping(FILE_MAP_READ
, FALSE
, bufMemName
);
795 if (bufMemHandle
!= NULL
) {
798 bufMemp
= MapViewOfFile(bufMemHandle
, FILE_MAP_READ
, 0, 0, 0);
800 if (bufMemp
!= NULL
) {
801 /* read data size and data from shared memory */
802 size_t *memp
= (size_t *) bufMemp
;
805 *datap
= malloc(*dataLen
);
807 if (*datap
!= NULL
) {
808 memcpy(*datap
, (void *)memp
, *dataLen
);
811 (void)UnmapViewOfFile(bufMemp
);
814 (void)CloseHandle(bufMemHandle
);
817 (void)SetEvent(bufEventHandle
);
818 (void)CloseHandle(bufEventHandle
);
831 * ChildMonitorThread() -- Thread spawned to monitor status of child process.
833 * Param must be index into child process table.
836 ChildMonitorThread(LPVOID param
)
838 int tidx
= (int)(intptr_t)param
;
839 HANDLE childProcHandle
;
843 /* retrieve handle for child process from process table and duplicate */
845 (void)pthread_mutex_lock(&procTableLock
);
847 fsuccess
= DuplicateHandle(GetCurrentProcess(), /* source process handle */
848 procTable
[tidx
].p_handle
, /* source handle to dup */
849 GetCurrentProcess(), /* target process handle */
850 &childProcHandle
, /* target handle (duplicate) */
851 0, /* access (ignored here) */
852 FALSE
, /* not inheritable */
853 DUPLICATE_SAME_ACCESS
);
855 (void)pthread_mutex_unlock(&procTableLock
);
858 /* wait for child process to terminate */
860 if (WaitForSingleObject(childProcHandle
, INFINITE
) == WAIT_OBJECT_0
) {
861 /* child process terminated; mark in table and signal event */
862 (void)pthread_mutex_lock(&procTableLock
);
864 procTable
[tidx
].p_terminated
= TRUE
;
865 (void)GetExitCodeProcess(childProcHandle
,
866 &procTable
[tidx
].p_status
);
869 (void)pthread_mutex_unlock(&procTableLock
);
871 (void)pthread_cond_broadcast(&childTermEvent
);
873 /* process/raise SIGCHLD; do last in case handler never returns */
874 ProcessSignal(SIGCHLD
);
878 (void)CloseHandle(childProcHandle
);
881 /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
882 * fail; however, this should never happen.
890 * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
892 * Returns pid of the child process ((pid_t)-1 on failure with errno set).
894 * Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
895 * Open files are not inherited; child's stdin, stdout, and stderr
896 * are set to parent's console.
897 * If spath does not specify a filename extension ".exe" is used.
898 * If sdatap is not NULL, and sdatalen > 0, data is passed to child.
899 * The spath and sargv[] strings must not contain quote chars (").
901 * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
904 pmgt_ProcessSpawnVEB(const char *spath
, char *sargv
[], char *senvp
[],
905 void *sdatap
, size_t sdatalen
)
908 char *pathbuf
, *argbuf
, *envbuf
;
909 char pathext
[_MAX_EXT
];
910 STARTUPINFO startInfo
;
911 PROCESS_INFORMATION procInfo
;
912 HANDLE monitorHandle
= NULL
;
913 HANDLE bufMemHandle
, bufEventHandle
;
914 DWORD monitorId
, createFlags
;
915 BOOL passingBuffer
= (sdatap
!= NULL
&& sdatalen
> 0);
919 /* verify arguments */
920 if (!spath
|| !sargv
) {
923 } else if (*spath
== '\0') {
928 /* create path with .exe extension if no filename extension supplied */
929 if (!(pathbuf
= malloc(strlen(spath
) + 5 /* .exe */ ))) {
931 return ((pid_t
) - 1);
933 strcpy(pathbuf
, spath
);
935 _splitpath(pathbuf
, NULL
, NULL
, NULL
, pathext
);
936 if (*pathext
== '\0') {
937 /* no filename extension supplied for spath; .exe is assumed */
938 strcat(pathbuf
, ".exe");
941 /* create command line argument string */
942 argbuf
= StringArrayToString(sargv
);
947 return ((pid_t
) - 1);
950 /* create environment variable block (multistring) */
952 /* use environment variables provided */
953 envbuf
= StringArrayToMultiString(senvp
);
959 return ((pid_t
) - 1);
962 /* use default environment variables */
966 /* set process creation flags */
967 createFlags
= CREATE_SUSPENDED
| NORMAL_PRIORITY_CLASS
;
969 if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME
) != NULL
) {
970 createFlags
|= DETACHED_PROCESS
;
973 /* clear start-up info; use defaults */
974 memset((void *)&startInfo
, 0, sizeof(startInfo
));
975 startInfo
.cb
= sizeof(startInfo
);
977 /* perform the following as a logically atomic unit:
978 * 1) allocate a process table entry
979 * 2) spawn child process (suspended)
980 * 3) create data buffer to pass (optional)
981 * 4) initialize process table entry
982 * 5) start child watcher thread
983 * 6) resume spawned child process
986 (void)pthread_mutex_lock(&procTableLock
);
988 for (tidx
= 0; tidx
< PMGT_CHILD_MAX
; tidx
++) {
989 if (procTable
[tidx
].p_handle
== NULL
990 && procTable
[tidx
].p_reserved
== FALSE
) {
991 procTable
[tidx
].p_reserved
= TRUE
;
995 (void)pthread_mutex_unlock(&procTableLock
);
997 if (tidx
>= PMGT_CHILD_MAX
) {
998 /* no space left in process table */
1007 fsuccess
= CreateProcess(pathbuf
, /* executable path */
1008 argbuf
, /* command line argument string */
1009 NULL
, /* default process security attr */
1010 NULL
, /* default thread security attr */
1011 FALSE
, /* do NOT inherit handles */
1012 createFlags
, /* creation control flags */
1013 envbuf
, /* environment variable block */
1014 NULL
, /* current directory is that of parent */
1015 &startInfo
, /* startup info block */
1018 lasterror
= GetLastError();
1024 /* failed to spawn process */
1025 errno
= nterr_nt2unix(lasterror
, ENOENT
);
1027 (void)pthread_mutex_lock(&procTableLock
);
1028 procTable
[tidx
].p_reserved
= FALSE
; /* mark entry as not reserved */
1029 (void)pthread_mutex_unlock(&procTableLock
);
1034 if (passingBuffer
) {
1035 /* create named data buffer and read event for child */
1037 CreateChildDataBuffer(procInfo
.dwProcessId
, sdatap
, sdatalen
,
1038 &bufMemHandle
, &bufEventHandle
);
1040 (void)pthread_mutex_lock(&procTableLock
);
1041 procTable
[tidx
].p_reserved
= FALSE
; /* mark entry 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
);
1054 (void)pthread_mutex_lock(&procTableLock
);
1056 procTable
[tidx
].p_handle
= procInfo
.hProcess
;
1057 procTable
[tidx
].p_id
= procInfo
.dwProcessId
;
1058 procTable
[tidx
].p_terminated
= FALSE
;
1062 /* Note: must hold procTableLock during monitor thread creation so
1063 * that if creation fails we can clean up process table before another
1064 * thread has a chance to see this procTable entry. Continue to hold
1065 * procTableLock while resuming child process, since the procTable
1066 * entry contains a copy of the child process handle which we might use.
1068 monitorHandle
= CreateThread(NULL
, /* default security attr. */
1069 0, /* default stack size */
1070 ChildMonitorThread
, (LPVOID
)(intptr_t) tidx
, /* thread argument */
1071 0, /* creation flags */
1072 &monitorId
); /* thread id */
1074 if (monitorHandle
== NULL
) {
1075 /* failed to start child monitor thread */
1076 procTable
[tidx
].p_handle
= NULL
; /* invalidate table entry */
1077 procTable
[tidx
].p_reserved
= FALSE
; /* mark entry as not reserved */
1080 (void)pthread_mutex_unlock(&procTableLock
);
1082 (void)TerminateProcess(procInfo
.hProcess
,
1083 PMGT_SIGSTATUS_ENCODE(SIGKILL
));
1084 (void)CloseHandle(procInfo
.hThread
);
1085 (void)CloseHandle(procInfo
.hProcess
);
1087 if (passingBuffer
) {
1088 (void)CloseHandle(bufMemHandle
);
1089 (void)CloseHandle(bufEventHandle
);
1096 /* Resume child process, which was created suspended to implement spawn
1097 * atomically. If resumption fails, which it never should, terminate
1098 * the child process with a status of SIGKILL. Spawn still succeeds and
1099 * the net result is the same as if the child process received a spurious
1100 * SIGKILL signal; the child monitor thread will then handle this.
1102 if (ResumeThread(procInfo
.hThread
) == DWORD_OF_ONES
) {
1103 (void)TerminateProcess(procInfo
.hProcess
,
1104 PMGT_SIGSTATUS_ENCODE(SIGKILL
));
1106 if (passingBuffer
) {
1107 /* child will never read data buffer */
1108 (void)SetEvent(bufEventHandle
);
1112 (void)pthread_mutex_unlock(&procTableLock
);
1114 (void)CloseHandle(procInfo
.hThread
);
1115 (void)CloseHandle(monitorHandle
);
1117 /* After spawn returns, signals can not be sent to the new child process
1118 * until that child initializes its signal-receiving mechanism (assuming
1119 * the child is linked with this library). Shorten (but sadly don't
1120 * eliminate) this window of opportunity for failure by yielding this
1121 * thread's time slice.
1123 (void)SwitchToThread();
1125 /* If passing a data buffer to child, wait until child reads buffer
1126 * before closing handles and thus freeing resources; if don't wait
1127 * then parent can not safely exit immediately after returning from
1128 * this call (hence why wait is not done in a background thread).
1130 if (passingBuffer
) {
1131 WaitForSingleObject(bufEventHandle
, 10000);
1132 /* note: if wait times out, child may not get to read buffer */
1133 (void)CloseHandle(bufMemHandle
);
1134 (void)CloseHandle(bufEventHandle
);
1137 return (pid_t
) procInfo
.dwProcessId
;
1143 * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1144 * for child to terminate (Unix waitpid() semantics).
1146 * Note: does not support waiting for process in group (i.e., pid
1147 * equals (pid_t)0 or pid is less than (pid_t)-1.
1150 pmgt_ProcessWaitPid(pid_t pid
, int *statusP
, int options
)
1154 BOOL statusFound
= FALSE
;
1157 /* validate arguments */
1158 if (pid
< (pid_t
) - 1 || pid
== (pid_t
) 0) {
1163 /* determine how long caller is willing to wait for child */
1165 waitTime
= (options
& WNOHANG
) ? 0 : INFINITE
;
1167 /* get child status */
1169 (void)pthread_mutex_lock(&procTableLock
);
1172 BOOL waitForChild
= FALSE
;
1174 if (procEntryCount
== 0) {
1175 /* no child processes */
1179 /* determine if status is available for specified child id */
1181 if (pid
== (pid_t
) - 1) {
1182 /* CASE 1: pid matches any child id */
1184 if (procTermCount
== 0) {
1185 /* status not available for any child ... */
1186 if (waitTime
== 0) {
1187 /* ... and caller is not willing to wait */
1190 /* ... but caller is willing to wait */
1191 waitForChild
= TRUE
;
1194 /* status available for some child; locate table entry */
1195 for (tidx
= 0; tidx
< PMGT_CHILD_MAX
; tidx
++) {
1196 if (procTable
[tidx
].p_handle
!= NULL
1197 && procTable
[tidx
].p_terminated
== TRUE
) {
1204 /* should never happen; indicates a bug */
1205 errno
= EINTR
; /* plausible lie for failure */
1211 /* CASE 2: pid must match a specific child id */
1213 /* locate table entry */
1214 for (tidx
= 0; tidx
< PMGT_CHILD_MAX
; tidx
++) {
1215 if (procTable
[tidx
].p_handle
!= NULL
1216 && procTable
[tidx
].p_id
== (DWORD
) pid
) {
1221 if (tidx
>= PMGT_CHILD_MAX
) {
1222 /* pid does not match any child id */
1225 } else if (procTable
[tidx
].p_terminated
== FALSE
) {
1226 /* status not available for specified child ... */
1227 if (waitTime
== 0) {
1228 /* ... and caller is not willing to wait */
1231 /* ... but caller is willing to wait */
1232 waitForChild
= TRUE
;
1235 /* status is available for specified child */
1242 (void)pthread_cond_wait(&childTermEvent
, &procTableLock
);
1249 /* child status available */
1251 *statusP
= ComputeWaitStatus(procTable
[tidx
].p_status
);
1253 rc
= (pid_t
) procTable
[tidx
].p_id
;
1255 /* clean up process table */
1256 (void)CloseHandle(procTable
[tidx
].p_handle
);
1257 procTable
[tidx
].p_handle
= NULL
;
1258 procTable
[tidx
].p_reserved
= FALSE
;
1264 (void)pthread_mutex_unlock(&procTableLock
);
1273 /* ----------------- General ---------------- */
1278 * PmgtLibraryInitialize() -- Initialize process management library.
1281 PmgtLibraryInitialize(void)
1284 HANDLE sigPipeHandle
;
1285 char sigPipeName
[sizeof(PMGT_SIGNAL_PIPE_PREFIX
) + 20];
1286 HANDLE sigListenerHandle
;
1287 DWORD sigListenerId
;
1289 /* initialize mutex locks and condition variables */
1291 if ((rc
= pthread_mutex_init(&signalTableLock
, NULL
))
1292 || (rc
= pthread_mutex_init(&signalBlockLock
, NULL
))
1293 || (rc
= pthread_mutex_init(&procTableLock
, NULL
))
1294 || (rc
= pthread_cond_init(&childTermEvent
, NULL
))) {
1299 /* initialize signal disposition table */
1301 for (i
= 0; i
< NSIG
; i
++) {
1302 if (SignalIsDefined(i
)) {
1303 /* initialize to default action for defined signals */
1304 signalTable
[i
].action
.sa_handler
= SIG_DFL
;
1305 sigemptyset(&signalTable
[i
].action
.sa_mask
);
1306 signalTable
[i
].action
.sa_flags
= 0;
1308 /* initialize to ignore for undefined signals */
1309 signalTable
[i
].action
.sa_handler
= SIG_IGN
;
1313 /* initialize child process table */
1315 for (i
= 0; i
< PMGT_CHILD_MAX
; i
++) {
1316 procTable
[i
].p_handle
= NULL
;
1317 procTable
[i
].p_reserved
= FALSE
;
1322 /* retrieve data buffer passed from parent in spawn, if any */
1324 if (!ReadChildDataBuffer(&pmgt_spawnData
, &pmgt_spawnDataLen
)) {
1325 pmgt_spawnData
= NULL
;
1326 pmgt_spawnDataLen
= 0;
1329 /* create named pipe for delivering signals to this process */
1331 sprintf(sigPipeName
, "%s%d", PMGT_SIGNAL_PIPE_PREFIX
,
1332 (int)GetCurrentProcessId());
1334 sigPipeHandle
= CreateNamedPipe(sigPipeName
, /* pipe for this process */
1335 PIPE_ACCESS_DUPLEX
| /* full duplex pipe */
1336 WRITE_DAC
, /* DACL write access */
1337 PIPE_TYPE_MESSAGE
| /* message type pipe */
1338 PIPE_READMODE_MESSAGE
| /* message read-mode */
1339 PIPE_WAIT
, /* blocking mode */
1340 1, /* max of 1 pipe instance */
1341 64, /* output buffer size (advisory) */
1342 64, /* input buffer size (advisory) */
1343 1000, /* 1 sec default client timeout */
1344 NULL
); /* default security attr. */
1346 if (sigPipeHandle
== INVALID_HANDLE_VALUE
) {
1347 /* failed to create signal pipe */
1348 errno
= nterr_nt2unix(GetLastError(), EIO
);
1352 /* add entry to signal pipe ACL granting local Administrators R/W access */
1354 (void)ObjectDaclEntryAdd(sigPipeHandle
, SE_KERNEL_OBJECT
,
1355 LocalAdministratorsGroup
,
1356 GENERIC_READ
| GENERIC_WRITE
, GRANT_ACCESS
,
1359 /* start signal pipe listener thread */
1361 sigListenerHandle
= CreateThread(NULL
, /* default security attr. */
1362 0, /* default stack size */
1363 RemoteSignalListenerThread
, (LPVOID
) sigPipeHandle
, /* thread argument */
1364 0, /* creation flags */
1365 &sigListenerId
); /* thread id */
1367 if (sigListenerHandle
!= NULL
) {
1368 /* listener thread started; bump priority */
1369 (void)SetThreadPriority(sigListenerHandle
, THREAD_PRIORITY_HIGHEST
);
1370 (void)CloseHandle(sigListenerHandle
);
1372 /* failed to start listener thread */
1374 (void)CloseHandle(sigPipeHandle
);
1378 /* redirect native NT signals into this process management library */
1380 if (pmgt_RedirectNativeSignals()) {
1381 /* errno set by called function */
1390 * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1391 * function in the MSVC runtime DLL (msvcrt.dll).
1393 * Note: the system serializes calls to this function.
1396 DllMain(HINSTANCE dllInstHandle
, /* instance handle for this DLL module */
1397 DWORD reason
, /* reason function is being called */
1399 { /* reserved for future use */
1401 case DLL_PROCESS_ATTACH
:
1402 /* library is being attached to a process */
1403 if (PmgtLibraryInitialize()) {
1404 /* failed to initialize library */
1408 /* disable thread attach/detach notifications */
1409 (void)DisableThreadLibraryCalls(dllInstHandle
);
1411 case DLL_PROCESS_DETACH
:
1412 pmgt_RestoreNativeSignals();