procmgmt: Tidy header includes
[openafs.git] / src / procmgmt / procmgmt_nt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include "procmgmt.h" /* Must be before roken */
15
16 #include <roken.h>
17
18 #include <windows.h>
19 #include <pthread.h>
20 #include <afs/errmap_nt.h>
21 #include <afs/secutil_nt.h>
22
23 #include "pmgtprivate.h"
24
25 /* Signal disposition table and associated definitions and locks */
26
27 typedef struct {
28     struct sigaction action;    /* signal action information */
29 } sigtable_entry_t;
30
31 static sigtable_entry_t signalTable[NSIG];      /* signal table; slot 0 unused */
32 static pthread_mutex_t signalTableLock; /* lock protects signalTable */
33
34 /* Global signal block lock; all signal handlers are serialized for now */
35 static pthread_mutex_t signalBlockLock;
36
37 /* Named pipe prefix for sending signals */
38 #define PMGT_SIGNAL_PIPE_PREFIX  "\\\\.\\pipe\\TransarcAfsSignalPipe"
39
40 /* Macro to test process exit status for an uncaught exception */
41 #define PMGT_IS_EXPSTATUS(status)   (((status) & 0xF0000000) == 0xC0000000)
42
43
44
45 /* Child process table and associated definitions and locks */
46
47 typedef struct {
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 */
53 } proctable_entry_t;
54
55 /* Max number of active child processes supported */
56 #define PMGT_CHILD_MAX  100
57
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 */
61
62 /* lock protects procTable, procEntryCount, and procTermCount */
63 static pthread_mutex_t procTableLock;
64
65 /* Named shared memory prefix for passing a data buffer to a child process */
66 #define PMGT_DATA_MEM_PREFIX  "TransarcAfsSpawnDataMemory"
67
68 /* Named event prefix for indicating that a data buffer has been read */
69 #define PMGT_DATA_EVENT_PREFIX  "TransarcAfsSpawnDataEvent"
70
71 /* event signals termination of a child process */
72 static pthread_cond_t childTermEvent;
73
74
75 /* Exported data values */
76
77 void *pmgt_spawnData = NULL;
78 size_t pmgt_spawnDataLen = 0;
79
80
81 /* General definitions */
82
83 #define DWORD_OF_ONES   ((DWORD)0xFFFFFFFF)     /* a common Win32 failure code */
84
85
86
87
88
89
90 /* -----------------  Signals  ---------------- */
91
92
93 /*
94  * SignalIsDefined() -- Determine if an integer value corresponds to a
95  *     signal value defined for this platform.
96  */
97 static int
98 SignalIsDefined(int signo)
99 {
100     int isDefined = 0;
101
102     if (signo >= 1 && signo <= (NSIG - 1)) {
103         /* value is in valid range; check specifics */
104         switch (signo) {
105         case SIGHUP:
106         case SIGINT:
107         case SIGQUIT:
108         case SIGILL:
109         case SIGABRT:
110         case SIGFPE:
111         case SIGKILL:
112         case SIGSEGV:
113         case SIGTERM:
114         case SIGUSR1:
115         case SIGUSR2:
116         case SIGCHLD:
117         case SIGTSTP:
118             isDefined = 1;
119             break;
120         }
121     }
122     return isDefined;
123 }
124
125
126 /*
127  * DefaultActionHandler() -- Execute the default action for the given signal.
128  */
129 static void __cdecl
130 DefaultActionHandler(int signo)
131 {
132     switch (signo) {
133     case SIGHUP:
134     case SIGINT:
135     case SIGKILL:
136     case SIGQUIT:
137     case SIGTERM:
138         /* default action is "exit" */
139         ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
140         break;
141     case SIGILL:
142     case SIGABRT:
143     case SIGFPE:
144     case SIGSEGV:
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.
150          */
151         RaiseException((DWORD) PMGT_SIGSTATUS_ENCODE(signo),
152                        EXCEPTION_NONCONTINUABLE, 0, NULL);
153         break;
154     case SIGUSR1:
155     case SIGUSR2:
156     case SIGCHLD:
157         /* default action is "ignore" */
158         break;
159     case SIGTSTP:
160         /* default action is "stop" */
161         /* No good way to implement this from inside a process so ignore */
162         break;
163     default:
164         /* no default action for specified signal value; just ignore */
165         break;
166     }
167 }
168
169
170 /*
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.
174  *
175  * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
176  */
177 static void
178 ProcessSignal(int signo)
179 {
180     struct sigaction sigEntry;
181
182     if (signo != SIGKILL) {
183         /* serialize signals, but never block processing of SIGKILL */
184         (void)pthread_mutex_lock(&signalBlockLock);
185     }
186
187     /* fetch disposition of signo, updating it if necessary */
188
189     (void)pthread_mutex_lock(&signalTableLock);
190     sigEntry = signalTable[signo].action;
191
192     if ((sigEntry.sa_handler != SIG_IGN) && (sigEntry.sa_flags & SA_RESETHAND)
193         && (signo != SIGILL)) {
194         signalTable[signo].action.sa_handler = SIG_DFL;
195     }
196     (void)pthread_mutex_unlock(&signalTableLock);
197
198     /* execute handler */
199
200     if (sigEntry.sa_handler != SIG_IGN) {
201         if (sigEntry.sa_handler == SIG_DFL) {
202             sigEntry.sa_handler = DefaultActionHandler;
203         }
204         (*sigEntry.sa_handler) (signo);
205     }
206
207     if (signo != SIGKILL) {
208         (void)pthread_mutex_unlock(&signalBlockLock);
209     }
210 }
211
212
213 /*
214  * RemoteSignalThread() -- Thread spawned to process remote signal.
215  *
216  *     Param must be the signal number.
217  */
218 static DWORD WINAPI
219 RemoteSignalThread(LPVOID param)
220 {
221     int signo = (int)(intptr_t)param;
222     DWORD rc = 0;
223
224     if (SignalIsDefined(signo)) {
225         /* process signal */
226         ProcessSignal(signo);
227     } else if (signo != 0) {
228         /* invalid signal value */
229         rc = -1;
230     }
231     return rc;
232 }
233
234
235 /*
236  * RemoteSignalListenerThread() -- Thread spawned to receive and process
237  *     remotely generated signals; never returns.
238  *
239  *     Param must be a handle for a duplex server message pipe in blocking
240  *     mode.
241  */
242 static DWORD WINAPI
243 RemoteSignalListenerThread(LPVOID param)
244 {
245     HANDLE sigPipeHandle = (HANDLE) param;
246     HMODULE hLib = LoadLibrary("AFSPROCMGMT.DLL");
247
248     while (1) {
249         /* wait for pipe client to connect */
250
251         if ((ConnectNamedPipe(sigPipeHandle, NULL))
252             || (GetLastError() == ERROR_PIPE_CONNECTED)) {
253             /* client connected; read signal value */
254             int signo;
255             DWORD bytesXfered;
256
257             if ((ReadFile
258                  (sigPipeHandle, &signo, sizeof(signo), &bytesXfered, NULL))
259                 && (bytesXfered == sizeof(signo))) {
260                 HANDLE sigThreadHandle;
261                 DWORD sigThreadId;
262
263                 /* ACK signal to release sender */
264                 (void)WriteFile(sigPipeHandle, &signo, sizeof(signo),
265                                 &bytesXfered, NULL);
266
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).
270                  */
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 */
276
277                 if (sigThreadHandle != NULL) {
278                     (void)CloseHandle(sigThreadHandle);
279                 }
280             }
281             /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
282
283         } else {
284             /* connect failed; this should never happen */
285             Sleep(2000);        /* sleep 2 seconds to avoid tight loop */
286         }
287
288         (void)DisconnectNamedPipe(sigPipeHandle);
289     }
290
291     /* never reached */
292     FreeLibrary(hLib);
293     return (0);
294 }
295
296
297
298
299
300 /*
301  * pmgt_SigactionSet() -- Examine and/or specify the action for a given
302  *     signal (Unix sigaction() semantics).
303  */
304 int
305 pmgt_SigactionSet(int signo, const struct sigaction *actionP,
306                   struct sigaction *old_actionP)
307 {
308     /* validate arguments */
309
310     if (!SignalIsDefined(signo) || signo == SIGKILL) {
311         /* invalid signal value or signal can't be caught/ignored */
312         errno = EINVAL;
313         return -1;
314     }
315
316     if (actionP && actionP->sa_handler == SIG_ERR) {
317         /* invalid signal disposition */
318         errno = EINVAL;
319         return -1;
320     }
321
322     /* fetch and/or set disposition of signo */
323
324     (void)pthread_mutex_lock(&signalTableLock);
325
326     if (old_actionP) {
327         *old_actionP = signalTable[signo].action;
328     }
329
330     if (actionP) {
331         signalTable[signo].action = *actionP;
332     }
333
334     (void)pthread_mutex_unlock(&signalTableLock);
335
336     return 0;
337 }
338
339
340 /*
341  * pmgt_SignalSet() -- Specify the disposition for a given signal
342  *     value (Unix signal() semantics).
343  */
344 void (__cdecl * pmgt_SignalSet(int signo, void (__cdecl * dispP) (int))) (int) {
345     struct sigaction newAction, oldAction;
346
347     /* construct action to request Unix signal() semantics */
348
349     newAction.sa_handler = dispP;
350     sigemptyset(&newAction.sa_mask);
351     newAction.sa_flags = SA_RESETHAND;
352
353     if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
354         /* successfully set new signal action */
355         return oldAction.sa_handler;
356     } else {
357         /* failed to set signal action; errno will have been set */
358         return SIG_ERR;
359     }
360 }
361
362
363 /*
364  * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
365  *     semantics).
366  */
367 int
368 pmgt_SignalRaiseLocal(int signo)
369 {
370     int rc = 0;
371
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.
375      */
376
377     if (SignalIsDefined(signo)) {
378         /* process signal */
379         ProcessSignal(signo);
380     } else if (signo != 0) {
381         /* invalid signal value */
382         errno = EINVAL;
383         rc = -1;
384     }
385     return rc;
386 }
387
388
389 /*
390  * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
391  *     the signal is specified by name (C raise() semantics).
392  *
393  *     Upon successful completion, *libSigno is set to the process management
394  *     library's constant value for signame.
395  *
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.
398  */
399 int
400 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
401 {
402     int rc = 0;
403     int signo;
404
405     if (!strcmp(signame, "SIGHUP")) {
406         signo = SIGHUP;
407     } else if (!strcmp(signame, "SIGINT")) {
408         signo = SIGINT;
409     } else if (!strcmp(signame, "SIGQUIT")) {
410         signo = SIGQUIT;
411     } else if (!strcmp(signame, "SIGILL")) {
412         signo = SIGILL;
413     } else if (!strcmp(signame, "SIGABRT")) {
414         signo = SIGABRT;
415     } else if (!strcmp(signame, "SIGFPE")) {
416         signo = SIGFPE;
417     } else if (!strcmp(signame, "SIGKILL")) {
418         signo = SIGKILL;
419     } else if (!strcmp(signame, "SIGSEGV")) {
420         signo = SIGSEGV;
421     } else if (!strcmp(signame, "SIGTERM")) {
422         signo = SIGTERM;
423     } else if (!strcmp(signame, "SIGUSR1")) {
424         signo = SIGUSR1;
425     } else if (!strcmp(signame, "SIGUSR2")) {
426         signo = SIGUSR2;
427     } else if (!strcmp(signame, "SIGCLD")) {
428         signo = SIGCLD;
429     } else if (!strcmp(signame, "SIGCHLD")) {
430         signo = SIGCHLD;
431     } else if (!strcmp(signame, "SIGTSTP")) {
432         signo = SIGTSTP;
433     } else {
434         /* unknown signal name */
435         errno = EINVAL;
436         rc = -1;
437     }
438
439     if (rc == 0) {
440         *libSigno = signo;
441         rc = pmgt_SignalRaiseLocal(signo);
442     }
443     return rc;
444 }
445
446
447 /*
448  * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
449  *     kill() semantics).
450  *
451  *     Note: only supports sending signal to a specific (single) process.
452  */
453 int
454 pmgt_SignalRaiseRemote(pid_t pid, int signo)
455 {
456     BOOL fsuccess;
457     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
458     DWORD ackBytesRead;
459     int signoACK;
460     int status = 0;
461
462     /* validate arguments */
463
464     if ((pid <= (pid_t) 0) || (!SignalIsDefined(signo) && signo != 0)) {
465         /* invalid pid or signo */
466         errno = EINVAL;
467         return -1;
468     }
469
470     /* optimize for the "this process" case */
471
472     if (pid == (pid_t) GetCurrentProcessId()) {
473         return pmgt_SignalRaiseLocal(signo);
474     }
475
476     /* send signal to process via named pipe */
477
478     sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
479
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 */
487
488     if (!fsuccess) {
489         /* failed to send signal via named pipe */
490         status = -1;
491
492         if (signo == SIGKILL) {
493             /* could be a non-AFS process, which might still be kill-able */
494             HANDLE procHandle;
495
496             if (procHandle =
497                 OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid)) {
498                 if (TerminateProcess
499                     (procHandle, PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
500                     /* successfully killed process */
501                     status = 0;
502                 } else {
503                     errno = nterr_nt2unix(GetLastError(), EPERM);
504                 }
505                 (void)CloseHandle(procHandle);
506             } else {
507                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
508                     errno = ESRCH;
509                 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
510                     errno = EPERM;
511                 } else {
512                     errno = nterr_nt2unix(GetLastError(), EPERM);
513                 }
514             }
515         } else {
516             /* couldn't open pipe so can't send (non-SIGKILL) signal */
517             errno = nterr_nt2unix(GetLastError(), EPERM);
518         }
519     }
520
521     return status;
522 }
523
524
525
526
527 /* -----------------  Processes  ---------------- */
528
529
530 /*
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).
535  */
536 static char *
537 StringArrayToString(char *strArray[])
538 {
539     int strCount = 0;
540     int byteCount = 0;
541     char *buffer = NULL;
542
543     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
544         /* sum all string lengths */
545         byteCount += (int)strlen(strArray[strCount]);
546     }
547
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) {
551         int i;
552
553         buffer[0] = '\0';
554
555         for (i = 0; i < strCount; i++) {
556             char *bufp = buffer + strlen(buffer);
557
558             if (i == strCount - 1) {
559                 /* last string; no trailing space */
560                 sprintf(bufp, "\"%s\"", strArray[i]);
561             } else {
562                 sprintf(bufp, "\"%s\" ", strArray[i]);
563             }
564         }
565     }
566
567     return (buffer);
568 }
569
570
571 /*
572  * StringArrayToMultiString() -- convert a null-terminated array of strings,
573  *     such as envp, into a multistring.
574  */
575 static char *
576 StringArrayToMultiString(char *strArray[])
577 {
578     int strCount = 0;
579     int byteCount = 0;
580     char *buffer = NULL;
581
582     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
583         /* sum all string lengths */
584         byteCount += strlen(strArray[strCount]);
585     }
586
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) {
591             buffer[0] = '\0';
592             buffer[1] = '\0';
593         } else {
594             int i;
595             char *bufp = buffer;
596
597             for (i = 0; i < strCount; i++) {
598                 int strLen = strlen(strArray[i]);
599
600                 if (strLen > 0) {
601                     /* can not embed zero length string in a multistring */
602                     strcpy(bufp, strArray[i]);
603                     bufp += strLen + 1;
604                 }
605             }
606             *bufp = '\0';       /* terminate multistring */
607         }
608     }
609
610     return (buffer);
611 }
612
613
614
615 /*
616  * ComputeWaitStatus() -- Compute an appropriate wait status value from
617  *     a given process termination (exit) code.
618  */
619 static int
620 ComputeWaitStatus(DWORD exitStatus)
621 {
622     int waitStatus;
623
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 */
630         int signo;
631
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:
642             signo = SIGFPE;
643             break;
644         case EXCEPTION_PRIV_INSTRUCTION:
645         case EXCEPTION_ILLEGAL_INSTRUCTION:
646             signo = SIGILL;
647             break;
648         case CONTROL_C_EXIT:
649             signo = SIGINT;
650             break;
651         default:
652             signo = SIGSEGV;
653             break;
654         }
655         waitStatus = WSIGNALED_ENCODE(signo);
656     } else {
657         /* child terminated normally */
658         waitStatus = WEXITED_ENCODE(exitStatus);
659     }
660
661     return waitStatus;
662 }
663
664
665
666 /*
667  * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
668  *     a child process, along with a corresponding buffer read event.
669  *
670  * ASSUMPTIONS: child process is linked with this process management library;
671  *     otherwise no data transfer will take place.
672  */
673 static BOOL
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];
684
685     sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
686     sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
687
688     /* Create and initialize named shared memory and named event */
689
690     *bufMemHandlep = CreateFileMapping(INVALID_HANDLE_VALUE,    /* page-file backed */
691                                        NULL, PAGE_READWRITE, 0, bufMemSize,
692                                        bufMemName);
693
694     if (*bufMemHandlep != NULL) {
695         void *bufMemp;
696
697         bufMemp =
698             MapViewOfFile(*bufMemHandlep, FILE_MAP_WRITE, 0, 0, bufMemSize);
699
700         if (bufMemp != NULL) {
701             /* copy data into shared memory, prefixed with data size */
702             size_t *memp = (size_t *) bufMemp;
703
704             *memp++ = dataLen;
705             memcpy((void *)memp, datap, dataLen);
706
707             if (UnmapViewOfFile(bufMemp)) {
708                 /* create buffer read event */
709                 *bufEventHandlep =
710                     CreateEvent(NULL, FALSE /* manual reset */ ,
711                                 FALSE /* initial state */ ,
712                                 bufEventName);
713                 if (*bufEventHandlep != NULL) {
714                     fsuccess = TRUE;
715                 }
716             }
717         }
718
719         if (!fsuccess) {
720             (void)CloseHandle(*bufMemHandlep);
721         }
722     }
723
724     if (!fsuccess) {
725         *bufMemHandlep = *bufEventHandlep = NULL;
726     }
727     return fsuccess;
728 }
729
730
731
732 /*
733  * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
734  *     if any, and place in allocated storage.
735  */
736 static BOOL
737 ReadChildDataBuffer(void **datap,       /* allocated data buffer */
738                     size_t * dataLen)
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;
744
745     sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX,
746             (int)GetCurrentProcessId());
747     sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX,
748             (int)GetCurrentProcessId());
749
750     /* Attempt to open named event and named shared memory */
751
752     bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
753
754     if (bufEventHandle != NULL) {
755         bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
756
757         if (bufMemHandle != NULL) {
758             void *bufMemp;
759
760             bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
761
762             if (bufMemp != NULL) {
763                 /* read data size and data from shared memory */
764                 size_t *memp = (size_t *) bufMemp;
765
766                 *dataLen = *memp++;
767                 *datap = (void *)malloc(*dataLen);
768
769                 if (*datap != NULL) {
770                     memcpy(*datap, (void *)memp, *dataLen);
771                     fsuccess = TRUE;
772                 }
773                 (void)UnmapViewOfFile(bufMemp);
774             }
775
776             (void)CloseHandle(bufMemHandle);
777         }
778
779         (void)SetEvent(bufEventHandle);
780         (void)CloseHandle(bufEventHandle);
781     }
782
783     if (!fsuccess) {
784         *datap = NULL;
785         *dataLen = 0;
786     }
787     return fsuccess;
788 }
789
790
791
792 /*
793  * ChildMonitorThread() -- Thread spawned to monitor status of child process.
794  *
795  *     Param must be index into child process table.
796  */
797 static DWORD WINAPI
798 ChildMonitorThread(LPVOID param)
799 {
800     int tidx = (int)(intptr_t)param;
801     HANDLE childProcHandle;
802     BOOL fsuccess;
803     DWORD rc = -1;
804
805     /* retrieve handle for child process from process table and duplicate */
806
807     (void)pthread_mutex_lock(&procTableLock);
808
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);
816
817     (void)pthread_mutex_unlock(&procTableLock);
818
819     if (fsuccess) {
820         /* wait for child process to terminate */
821
822         if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
823             /* child process terminated; mark in table and signal event */
824             (void)pthread_mutex_lock(&procTableLock);
825
826             procTable[tidx].p_terminated = TRUE;
827             (void)GetExitCodeProcess(childProcHandle,
828                                      &procTable[tidx].p_status);
829             procTermCount++;
830
831             (void)pthread_mutex_unlock(&procTableLock);
832
833             (void)pthread_cond_broadcast(&childTermEvent);
834
835             /* process/raise SIGCHLD; do last in case handler never returns */
836             ProcessSignal(SIGCHLD);
837             rc = 0;
838         }
839
840         (void)CloseHandle(childProcHandle);
841     }
842
843     /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
844      *       fail; however, this should never happen.
845      */
846     return rc;
847 }
848
849
850
851 /*
852  * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
853  *
854  *     Returns pid of the child process ((pid_t)-1 on failure with errno set).
855  *
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 (").
862  *
863  * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
864  */
865 pid_t
866 pmgt_ProcessSpawnVEB(const char *spath, char *sargv[], char *senvp[],
867                      void *sdatap, size_t sdatalen)
868 {
869     int tidx;
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);
878     BOOL fsuccess;
879     int lasterror;
880
881     /* verify arguments */
882     if (!spath || !sargv) {
883         errno = EFAULT;
884         return (pid_t) - 1;
885     } else if (*spath == '\0') {
886         errno = ENOENT;
887         return (pid_t) - 1;
888     }
889
890     /* create path with .exe extension if no filename extension supplied */
891     if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */ ))) {
892         errno = ENOMEM;
893         return ((pid_t) - 1);
894     }
895     strcpy(pathbuf, spath);
896
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");
901     }
902
903     /* create command line argument string */
904     argbuf = StringArrayToString(sargv);
905
906     if (!argbuf) {
907         free(pathbuf);
908         errno = ENOMEM;
909         return ((pid_t) - 1);
910     }
911
912     /* create environment variable block (multistring) */
913     if (senvp) {
914         /* use environment variables provided */
915         envbuf = StringArrayToMultiString(senvp);
916
917         if (!envbuf) {
918             free(pathbuf);
919             free(argbuf);
920             errno = ENOMEM;
921             return ((pid_t) - 1);
922         }
923     } else {
924         /* use default environment variables */
925         envbuf = NULL;
926     }
927
928     /* set process creation flags */
929     createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
930
931     if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
932         createFlags |= DETACHED_PROCESS;
933     }
934
935     /* clear start-up info; use defaults */
936     memset((void *)&startInfo, 0, sizeof(startInfo));
937     startInfo.cb = sizeof(startInfo);
938
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
946      */
947
948     (void)pthread_mutex_lock(&procTableLock);
949
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;
954             break;
955         }
956     }
957     (void)pthread_mutex_unlock(&procTableLock);
958
959     if (tidx >= PMGT_CHILD_MAX) {
960         /* no space left in process table */
961         free(pathbuf);
962         free(argbuf);
963         free(envbuf);
964
965         errno = EAGAIN;
966         return (pid_t) - 1;
967     }
968
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 */
978                              &procInfo);
979
980     lasterror = GetLastError();
981     free(pathbuf);
982     free(argbuf);
983     free(envbuf);
984
985     if (!fsuccess) {
986         /* failed to spawn process */
987         errno = nterr_nt2unix(lasterror, ENOENT);
988
989         (void)pthread_mutex_lock(&procTableLock);
990         procTable[tidx].p_reserved = FALSE;     /* mark entry as not reserved */
991         (void)pthread_mutex_unlock(&procTableLock);
992
993         return (pid_t) - 1;
994     }
995
996     if (passingBuffer) {
997         /* create named data buffer and read event for child */
998         fsuccess =
999             CreateChildDataBuffer(procInfo.dwProcessId, sdatap, sdatalen,
1000                                   &bufMemHandle, &bufEventHandle);
1001         if (!fsuccess) {
1002             (void)pthread_mutex_lock(&procTableLock);
1003             procTable[tidx].p_reserved = FALSE; /* mark entry not reserved */
1004             (void)pthread_mutex_unlock(&procTableLock);
1005
1006             (void)TerminateProcess(procInfo.hProcess,
1007                                    PMGT_SIGSTATUS_ENCODE(SIGKILL));
1008             (void)CloseHandle(procInfo.hThread);
1009             (void)CloseHandle(procInfo.hProcess);
1010
1011             errno = EAGAIN;
1012             return (pid_t) - 1;
1013         }
1014     }
1015
1016     (void)pthread_mutex_lock(&procTableLock);
1017
1018     procTable[tidx].p_handle = procInfo.hProcess;
1019     procTable[tidx].p_id = procInfo.dwProcessId;
1020     procTable[tidx].p_terminated = FALSE;
1021
1022     procEntryCount++;
1023
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.
1029      */
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 */
1035
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 */
1040         procEntryCount--;
1041
1042         (void)pthread_mutex_unlock(&procTableLock);
1043
1044         (void)TerminateProcess(procInfo.hProcess,
1045                                PMGT_SIGSTATUS_ENCODE(SIGKILL));
1046         (void)CloseHandle(procInfo.hThread);
1047         (void)CloseHandle(procInfo.hProcess);
1048
1049         if (passingBuffer) {
1050             (void)CloseHandle(bufMemHandle);
1051             (void)CloseHandle(bufEventHandle);
1052         }
1053
1054         errno = EAGAIN;
1055         return (pid_t) - 1;
1056     }
1057
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.
1063      */
1064     if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1065         (void)TerminateProcess(procInfo.hProcess,
1066                                PMGT_SIGSTATUS_ENCODE(SIGKILL));
1067
1068         if (passingBuffer) {
1069             /* child will never read data buffer */
1070             (void)SetEvent(bufEventHandle);
1071         }
1072     }
1073
1074     (void)pthread_mutex_unlock(&procTableLock);
1075
1076     (void)CloseHandle(procInfo.hThread);
1077     (void)CloseHandle(monitorHandle);
1078
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.
1084      */
1085     (void)SwitchToThread();
1086
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).
1091      */
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);
1097     }
1098
1099     return (pid_t) procInfo.dwProcessId;
1100 }
1101
1102
1103
1104 /*
1105  * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1106  *     for child to terminate (Unix waitpid() semantics).
1107  *
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.
1110  */
1111 pid_t
1112 pmgt_ProcessWaitPid(pid_t pid, int *statusP, int options)
1113 {
1114     pid_t rc;
1115     int tidx;
1116     BOOL statusFound = FALSE;
1117     DWORD waitTime;
1118
1119     /* validate arguments */
1120     if (pid < (pid_t) - 1 || pid == (pid_t) 0) {
1121         errno = EINVAL;
1122         return (pid_t) - 1;
1123     }
1124
1125     /* determine how long caller is willing to wait for child */
1126
1127     waitTime = (options & WNOHANG) ? 0 : INFINITE;
1128
1129     /* get child status */
1130
1131     (void)pthread_mutex_lock(&procTableLock);
1132
1133     while (1) {
1134         BOOL waitForChild = FALSE;
1135
1136         if (procEntryCount == 0) {
1137             /* no child processes */
1138             errno = ECHILD;
1139             rc = (pid_t) - 1;
1140         } else {
1141             /* determine if status is available for specified child id */
1142
1143             if (pid == (pid_t) - 1) {
1144                 /* CASE 1: pid matches any child id */
1145
1146                 if (procTermCount == 0) {
1147                     /* status not available for any child ... */
1148                     if (waitTime == 0) {
1149                         /* ... and caller is not willing to wait */
1150                         rc = (pid_t) 0;
1151                     } else {
1152                         /* ... but caller is willing to wait */
1153                         waitForChild = TRUE;
1154                     }
1155                 } else {
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) {
1160                             statusFound = TRUE;
1161                             break;
1162                         }
1163                     }
1164
1165                     if (!statusFound) {
1166                         /* should never happen; indicates a bug */
1167                         errno = EINTR;  /* plausible lie for failure */
1168                         rc = (pid_t) - 1;
1169                     }
1170                 }
1171
1172             } else {
1173                 /* CASE 2: pid must match a specific child id */
1174
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) {
1179                         break;
1180                     }
1181                 }
1182
1183                 if (tidx >= PMGT_CHILD_MAX) {
1184                     /* pid does not match any child id */
1185                     errno = ECHILD;
1186                     rc = (pid_t) - 1;
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 */
1191                         rc = (pid_t) 0;
1192                     } else {
1193                         /* ... but caller is willing to wait */
1194                         waitForChild = TRUE;
1195                     }
1196                 } else {
1197                     /* status is available for specified child */
1198                     statusFound = TRUE;
1199                 }
1200             }
1201         }
1202
1203         if (waitForChild) {
1204             (void)pthread_cond_wait(&childTermEvent, &procTableLock);
1205         } else {
1206             break;
1207         }
1208     }                           /* while() */
1209
1210     if (statusFound) {
1211         /* child status available */
1212         if (statusP) {
1213             *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1214         }
1215         rc = (pid_t) procTable[tidx].p_id;
1216
1217         /* clean up process table */
1218         (void)CloseHandle(procTable[tidx].p_handle);
1219         procTable[tidx].p_handle = NULL;
1220         procTable[tidx].p_reserved = FALSE;
1221
1222         procEntryCount--;
1223         procTermCount--;
1224     }
1225
1226     (void)pthread_mutex_unlock(&procTableLock);
1227     return rc;
1228 }
1229
1230
1231
1232
1233
1234
1235 /* -----------------  General  ---------------- */
1236
1237
1238
1239 /*
1240  * PmgtLibraryInitialize() -- Initialize process management library.
1241  */
1242 static int
1243 PmgtLibraryInitialize(void)
1244 {
1245     int rc, i;
1246     HANDLE sigPipeHandle;
1247     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1248     HANDLE sigListenerHandle;
1249     DWORD sigListenerId;
1250
1251     /* initialize mutex locks and condition variables */
1252
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))) {
1257         errno = rc;
1258         return -1;
1259     }
1260
1261     /* initialize signal disposition table */
1262
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;
1269         } else {
1270             /* initialize to ignore for undefined signals */
1271             signalTable[i].action.sa_handler = SIG_IGN;
1272         }
1273     }
1274
1275     /* initialize child process table */
1276
1277     for (i = 0; i < PMGT_CHILD_MAX; i++) {
1278         procTable[i].p_handle = NULL;
1279         procTable[i].p_reserved = FALSE;
1280     }
1281     procEntryCount = 0;
1282     procTermCount = 0;
1283
1284     /* retrieve data buffer passed from parent in spawn, if any */
1285
1286     if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1287         pmgt_spawnData = NULL;
1288         pmgt_spawnDataLen = 0;
1289     }
1290
1291     /* create named pipe for delivering signals to this process */
1292
1293     sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX,
1294             (int)GetCurrentProcessId());
1295
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. */
1307
1308     if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1309         /* failed to create signal pipe */
1310         errno = nterr_nt2unix(GetLastError(), EIO);
1311         return -1;
1312     }
1313
1314     /* add entry to signal pipe ACL granting local Administrators R/W access */
1315
1316     (void)ObjectDaclEntryAdd(sigPipeHandle, SE_KERNEL_OBJECT,
1317                              LocalAdministratorsGroup,
1318                              GENERIC_READ | GENERIC_WRITE, GRANT_ACCESS,
1319                              NO_INHERITANCE);
1320
1321     /* start signal pipe listener thread */
1322
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 */
1328
1329     if (sigListenerHandle != NULL) {
1330         /* listener thread started; bump priority */
1331         (void)SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1332         (void)CloseHandle(sigListenerHandle);
1333     } else {
1334         /* failed to start listener thread */
1335         errno = EAGAIN;
1336         (void)CloseHandle(sigPipeHandle);
1337         return -1;
1338     }
1339
1340     /* redirect native NT signals into this process management library */
1341
1342     if (pmgt_RedirectNativeSignals()) {
1343         /* errno set by called function */
1344         return -1;
1345     }
1346
1347     return 0;
1348 }
1349
1350
1351 /*
1352  * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1353  *     function in the MSVC runtime DLL (msvcrt.dll).
1354  *
1355  *     Note: the system serializes calls to this function.
1356  */
1357 BOOL WINAPI
1358 DllMain(HINSTANCE dllInstHandle,        /* instance handle for this DLL module */
1359         DWORD reason,           /* reason function is being called */
1360         LPVOID reserved)
1361 {                               /* reserved for future use */
1362     switch (reason) {
1363     case DLL_PROCESS_ATTACH:
1364         /* library is being attached to a process */
1365         if (PmgtLibraryInitialize()) {
1366             /* failed to initialize library */
1367             return FALSE;
1368         }
1369
1370         /* disable thread attach/detach notifications */
1371         (void)DisableThreadLibraryCalls(dllInstHandle);
1372         return TRUE;
1373     case DLL_PROCESS_DETACH:
1374         pmgt_RestoreNativeSignals();
1375         return TRUE;
1376     default:
1377         return FALSE;
1378     }
1379 }