772d8ede257aa505d9987884716d6be17e65873d
[openafs.git] / src / procmgmt / procmgmt_nt.c
1 /* Copyright (C) 1998  Transarc Corporation.  All Rights Reserved.
2  *
3  */
4
5
6 #include <afs/param.h>
7 #include <afs/stds.h>
8
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <windows.h>
15 #include <pthread.h>
16 #include <afs/errmap_nt.h>
17 #include <afs/secutil_nt.h>
18
19 #include "procmgmt.h"
20 #include "pmgtprivate.h"
21
22
23
24 /* Signal disposition table and associated definitions and locks */
25
26 typedef struct {
27     struct sigaction action;  /* signal action information */
28 } sigtable_entry_t;
29
30 static sigtable_entry_t signalTable[NSIG];  /* signal table; slot 0 unused */
31 static pthread_mutex_t  signalTableLock;    /* lock protects signalTable */
32
33 /* Global signal block lock; all signal handlers are serialized for now */
34 static pthread_mutex_t signalBlockLock;
35
36 /* Named pipe prefix for sending signals */
37 #define PMGT_SIGNAL_PIPE_PREFIX  "\\\\.\\pipe\\TransarcAfsSignalPipe"
38
39 /* Macro to test process exit status for an uncaught exception */
40 #define PMGT_IS_EXPSTATUS(status)   (((status) & 0xF0000000) == 0xC0000000)
41
42
43
44 /* Child process table and associated definitions and locks */
45
46 typedef struct {
47     HANDLE p_handle;      /* process handle (NULL if table entry not valid) */
48     BOOL   p_reserved;    /* table entry is reserved for spawn */
49     DWORD  p_id;          /* process id */
50     BOOL   p_terminated;  /* process terminated; status available/valid */
51     DWORD  p_status;      /* status of terminated process */
52 } proctable_entry_t;
53
54 /* Max number of active child processes supported */
55 #define PMGT_CHILD_MAX  100
56
57 static proctable_entry_t procTable[PMGT_CHILD_MAX];  /* child process table */
58 static int procEntryCount;   /* count of valid entries in procTable */
59 static int procTermCount;    /* count of terminated entries in procTable */
60
61 /* lock protects procTable, procEntryCount, and procTermCount */
62 static pthread_mutex_t procTableLock;
63
64 /* Named shared memory prefix for passing a data buffer to a child process */
65 #define PMGT_DATA_MEM_PREFIX  "TransarcAfsSpawnDataMemory"
66
67 /* Named event prefix for indicating that a data buffer has been read */
68 #define PMGT_DATA_EVENT_PREFIX  "TransarcAfsSpawnDataEvent"
69
70 /* event signals termination of a child process */
71 static pthread_cond_t childTermEvent;
72
73
74 /* Exported data values */
75
76 void *pmgt_spawnData = NULL;
77 size_t pmgt_spawnDataLen = 0;
78
79
80 /* General definitions */
81
82 #define DWORD_OF_ONES   ((DWORD)0xFFFFFFFF)  /* a common Win32 failure code */
83
84
85
86
87
88
89 /* -----------------  Signals  ---------------- */
90
91
92 /*
93  * SignalIsDefined() -- Determine if an integer value corresponds to a
94  *     signal value defined for this platform.
95  */
96 static int
97 SignalIsDefined(int signo)
98 {
99     int isDefined = 0;
100
101     if (signo >= 1 && signo <= (NSIG - 1)) {
102         /* value is in valid range; check specifics */
103         switch (signo) {
104           case SIGHUP:
105           case SIGINT:
106           case SIGQUIT:
107           case SIGILL:
108           case SIGABRT:
109           case SIGFPE:
110           case SIGKILL:
111           case SIGSEGV:
112           case SIGTERM:
113           case SIGUSR1:
114           case SIGUSR2:
115           case SIGCHLD:
116           case SIGTSTP:
117             isDefined = 1;
118             break;
119         }
120     }
121     return isDefined;
122 }
123
124
125 /*
126  * DefaultActionHandler() -- Execute the default action for the given signal.
127  */
128 static void __cdecl
129 DefaultActionHandler(int signo)
130 {
131     switch (signo) {
132       case SIGHUP:
133       case SIGINT:
134       case SIGKILL:
135       case SIGTERM:
136       case SIGUSR1:
137       case SIGUSR2:
138         /* default action is "exit" */
139         ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
140         break;
141       case SIGQUIT:
142       case SIGILL:
143       case SIGABRT:
144       case SIGFPE:
145       case SIGSEGV:
146         /* default action is "core" */
147         /* Best we can do is to raise an exception that can be caught by
148          * Dr. Watson, which can in turn generate a crash dump file.
149          * The default exception handler will call ExitProcess() with
150          * our application-specific exception code.
151          */
152         RaiseException((DWORD)PMGT_SIGSTATUS_ENCODE(signo),
153                        EXCEPTION_NONCONTINUABLE, 0, NULL);
154         break;
155       case SIGCHLD:
156         /* default action is "ignore" */
157         break;
158       case SIGTSTP:
159         /* default action is "stop" */
160         /* No good way to implement this from inside a process so ignore */
161         break;
162       default:
163         /* no default action for specified signal value; just ignore */
164         break;
165     }
166 }
167
168
169 /*
170  * ProcessSignal() -- Execute the specified or default handler for the given
171  *     signal; reset the signal's disposition to SIG_DFL if necessary.
172  *     If the signal's disposition is SIG_IGN then no processing takes place.
173  *
174  * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
175  */
176 static void
177 ProcessSignal(int signo)
178 {
179     struct sigaction sigEntry;
180
181     if (signo != SIGKILL) {
182         /* serialize signals, but never block processing of SIGKILL */
183         (void) pthread_mutex_lock(&signalBlockLock);
184     }
185
186     /* fetch disposition of signo, updating it if necessary */
187
188     (void) pthread_mutex_lock(&signalTableLock);
189     sigEntry = signalTable[signo].action;
190
191     if ((sigEntry.sa_handler != SIG_IGN) &&
192         (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)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
247     while (1) {
248         /* wait for pipe client to connect */
249
250         if ((ConnectNamedPipe(sigPipeHandle, NULL)) ||
251             (GetLastError() == ERROR_PIPE_CONNECTED)) {
252             /* client connected; read signal value */
253             int signo;
254             DWORD bytesXfered;
255
256             if ((ReadFile(sigPipeHandle,
257                           &signo, sizeof(signo), &bytesXfered, NULL)) &&
258                 (bytesXfered == sizeof(signo))) {
259                 HANDLE sigThreadHandle;
260                 DWORD sigThreadId;
261
262                 /* ACK signal to release sender */
263                 (void) WriteFile(sigPipeHandle,
264                                  &signo, sizeof(signo), &bytesXfered, NULL);
265
266                 /* spawn thread to process signal; we do this so that
267                  * we can always process a SIGKILL even if a signal handler
268                  * invoked earlier fails to return (blocked/spinning).
269                  */
270                 sigThreadHandle =
271                     CreateThread(NULL,           /* default security attr. */
272                                  0,              /* default stack size */
273                                  RemoteSignalThread,
274                                  (LPVOID)signo,  /* thread argument */
275                                  0,              /* creation flags */
276                                  &sigThreadId);  /* thread id */
277
278                 if (sigThreadHandle != NULL) {
279                     (void) CloseHandle(sigThreadHandle);
280                 }
281             }
282             /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
283
284         } else {
285             /* connect failed; this should never happen */
286             Sleep(2000); /* sleep 2 seconds to avoid tight loop */
287         }
288
289         (void)DisconnectNamedPipe(sigPipeHandle);
290     }
291
292     /* never reached */
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,
306                   const struct sigaction *actionP,
307                   struct sigaction *old_actionP)
308 {
309     /* validate arguments */
310
311     if (!SignalIsDefined(signo) || signo == SIGKILL) {
312         /* invalid signal value or signal can't be caught/ignored */
313         errno = EINVAL;
314         return -1;
315     }
316
317     if (actionP && actionP->sa_handler == SIG_ERR) {
318         /* invalid signal disposition */
319         errno = EINVAL;
320         return -1;
321     }
322
323     /* fetch and/or set disposition of signo */
324
325     (void) pthread_mutex_lock(&signalTableLock);
326
327     if (old_actionP) {
328         *old_actionP = signalTable[signo].action;
329     }
330
331     if (actionP) {
332         signalTable[signo].action = *actionP;
333     }
334
335     (void) pthread_mutex_unlock(&signalTableLock);
336
337     return 0;
338 }
339
340
341 /*
342  * pmgt_SignalSet() -- Specify the disposition for a given signal
343  *     value (Unix signal() semantics).
344  */
345 void (__cdecl *pmgt_SignalSet(int signo,
346                               void (__cdecl *dispP)(int)))(int)
347 {
348     struct sigaction newAction, oldAction;
349
350     /* construct action to request Unix signal() semantics */
351
352     newAction.sa_handler = dispP;
353     sigemptyset(&newAction.sa_mask);
354     newAction.sa_flags = SA_RESETHAND;
355
356     if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
357         /* successfully set new signal action */
358         return oldAction.sa_handler;
359     } else {
360         /* failed to set signal action; errno will have been set */
361         return SIG_ERR;
362     }
363 }
364
365
366 /*
367  * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
368  *     semantics).
369  */
370 int
371 pmgt_SignalRaiseLocal(int signo)
372 {
373     int rc = 0;
374
375     /* Process signal directly in the context of the calling thread.
376      * This is the same as if the signal had been raised in this process
377      * and this thread chosen to execute the handler.
378      */
379
380     if (SignalIsDefined(signo)) {
381         /* process signal */
382         ProcessSignal(signo);
383     } else if (signo != 0) {
384         /* invalid signal value */
385         errno = EINVAL;
386         rc = -1;
387     }
388     return rc;
389 }
390
391
392 /*
393  * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
394  *     the signal is specified by name (C raise() semantics).
395  *
396  *     Upon successful completion, *libSigno is set to the process management
397  *     library's constant value for signame.
398  *
399  *     Note: exists to implement the native-signal redirector (redirect_nt.c),
400  *           which can't include procmgmt.h and hence can't get the SIG* decls.
401  */
402 int
403 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
404 {
405     int rc = 0;
406     int signo;
407
408     if (!strcmp(signame, "SIGHUP")) {
409         signo = SIGHUP;
410     } else if (!strcmp(signame, "SIGINT")) {
411         signo = SIGINT;
412     } else if (!strcmp(signame, "SIGQUIT")) {
413         signo = SIGQUIT;
414     } else if (!strcmp(signame, "SIGILL")) {
415         signo = SIGILL;
416     } else if (!strcmp(signame, "SIGABRT")) {
417         signo = SIGABRT;
418     } else if (!strcmp(signame, "SIGFPE")) {
419         signo = SIGFPE;
420     } else if (!strcmp(signame, "SIGKILL")) {
421         signo = SIGKILL;
422     } else if (!strcmp(signame, "SIGSEGV")) {
423         signo = SIGSEGV;
424     } else if (!strcmp(signame, "SIGTERM")) {
425         signo = SIGTERM;
426     } else if (!strcmp(signame, "SIGUSR1")) {
427         signo = SIGUSR1;
428     } else if (!strcmp(signame, "SIGUSR2")) {
429         signo = SIGUSR2;
430     } else if (!strcmp(signame, "SIGCLD")) {
431         signo = SIGCLD;
432     } else if (!strcmp(signame, "SIGCHLD")) {
433         signo = SIGCHLD;
434     } else if (!strcmp(signame, "SIGTSTP")) {
435         signo = SIGTSTP;
436     } else {
437         /* unknown signal name */
438         errno = EINVAL;
439         rc = -1;
440     }
441
442     if (rc == 0) {
443         *libSigno = signo;
444         rc = pmgt_SignalRaiseLocal(signo);
445     }
446     return rc;
447 }
448
449
450 /*
451  * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
452  *     kill() semantics).
453  *
454  *     Note: only supports sending signal to a specific (single) process.
455  */
456 int
457 pmgt_SignalRaiseRemote(pid_t pid,
458                        int signo)
459 {
460     BOOL fsuccess;
461     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
462     DWORD ackBytesRead;
463     int signoACK;
464     int status = 0;
465
466     /* validate arguments */
467
468     if ((pid <= (pid_t)0) ||
469         (!SignalIsDefined(signo) && signo != 0)) {
470         /* invalid pid or signo */
471         errno = EINVAL;
472         return -1;
473     }
474
475     /* optimize for the "this process" case */
476
477     if (pid == (pid_t)GetCurrentProcessId()) {
478         return pmgt_SignalRaiseLocal(signo);
479     }
480
481     /* send signal to process via named pipe */
482
483     sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
484
485     fsuccess =
486         CallNamedPipe(sigPipeName,         /* process pid's signal pipe */
487                       &signo,              /* data written to pipe */
488                       sizeof(signo),       /* size of data to write */
489                       &signoACK,           /* data read from pipe */
490                       sizeof(signoACK),    /* size of data read buffer */
491                       &ackBytesRead,       /* number of bytes actually read */
492                       5 * 1000);           /* 5 second timeout */
493
494     if (!fsuccess) {
495         /* failed to send signal via named pipe */
496         status = -1;
497
498         if (signo == SIGKILL) {
499             /* could be a non-AFS process, which might still be kill-able */
500             HANDLE procHandle;
501
502             if (procHandle = OpenProcess(PROCESS_TERMINATE,
503                                          FALSE, (DWORD)pid)) {
504                 if (TerminateProcess(procHandle,
505                                      PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
506                     /* successfully killed process */
507                     status = 0;
508                 } else {
509                     errno = nterr_nt2unix(GetLastError(), EPERM);
510                 }
511                 (void) CloseHandle(procHandle);
512             } else {
513                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
514                     errno = ESRCH;
515                 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
516                     errno = EPERM;
517                 } else {
518                     errno = nterr_nt2unix(GetLastError(), EPERM);
519                 }
520             }
521         } else {
522             /* couldn't open pipe so can't send (non-SIGKILL) signal */
523             errno = nterr_nt2unix(GetLastError(), EPERM);
524         }
525     }
526
527     return status;
528 }
529
530
531
532
533 /* -----------------  Processes  ---------------- */
534
535
536 /*
537  * StringArrayToString() -- convert a null-terminated array of strings,
538  *     such as argv, into a single string of space-separated elements
539  *     with each element quoted (in case it contains space characters
540  *     or is of zero length).
541  */
542 static char *
543 StringArrayToString(char *strArray[])
544 {
545     int strCount = 0;
546     int byteCount = 0;
547     char *buffer = NULL;
548
549     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
550         /* sum all string lengths */
551         byteCount += strlen(strArray[strCount]);
552     }
553
554     /* put all strings into buffer; guarantee buffer is at least one char */
555     buffer = (char *)malloc(byteCount + (strCount * 3) /* quotes+space */ + 1);
556     if (buffer != NULL) {
557         int i;
558
559         buffer[0] = '\0';
560
561         for (i = 0; i < strCount; i++) {
562             char *bufp = buffer + strlen(buffer);
563
564             if (i == strCount - 1) {
565                 /* last string; no trailing space */
566                 sprintf(bufp, "\"%s\"", strArray[i]);
567             } else {
568                 sprintf(bufp, "\"%s\" ", strArray[i]);
569             }
570         }
571     }
572
573     return (buffer);
574 }
575
576
577 /*
578  * StringArrayToMultiString() -- convert a null-terminated array of strings,
579  *     such as envp, into a multistring.
580  */
581 static char *
582 StringArrayToMultiString(char *strArray[])
583 {
584     int strCount = 0;
585     int byteCount = 0;
586     char *buffer = NULL;
587
588     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
589         /* sum all string lengths */
590         byteCount += strlen(strArray[strCount]);
591     }
592
593     /* put all strings into buffer; guarantee buffer is at least two chars */
594     buffer = (char *)malloc(byteCount + strCount + 2);
595     if (buffer != NULL) {
596         if (byteCount == 0) {
597             buffer[0] = '\0';
598             buffer[1] = '\0';
599         } else {
600             int i;
601             char *bufp = buffer;
602
603             for (i = 0; i < strCount; i++) {
604                 int strLen = strlen(strArray[i]);
605
606                 if (strLen > 0) {
607                     /* can not embed zero length string in a multistring */
608                     strcpy(bufp, strArray[i]);
609                     bufp += strLen + 1;
610                 }
611             }
612             bufp = '\0';  /* terminate multistring */
613         }
614     }
615
616     return (buffer);
617 }
618
619
620
621 /*
622  * ComputeWaitStatus() -- Compute an appropriate wait status value from
623  *     a given process termination (exit) code.
624  */
625 static int
626 ComputeWaitStatus(DWORD exitStatus)
627 {
628     int waitStatus;
629
630     if (PMGT_IS_SIGSTATUS(exitStatus)) {
631         /* child terminated due to an unhandled signal */
632         int signo = PMGT_SIGSTATUS_DECODE(exitStatus);
633         waitStatus = WSIGNALED_ENCODE(signo);
634     } else if (PMGT_IS_EXPSTATUS(exitStatus)) {
635         /* child terminated due to an uncaught exception */
636         int signo;
637
638         switch (exitStatus) {
639           case EXCEPTION_FLT_DENORMAL_OPERAND:
640           case EXCEPTION_FLT_DIVIDE_BY_ZERO:
641           case EXCEPTION_FLT_INEXACT_RESULT:
642           case EXCEPTION_FLT_INVALID_OPERATION:
643           case EXCEPTION_FLT_OVERFLOW:
644           case EXCEPTION_FLT_STACK_CHECK:
645           case EXCEPTION_FLT_UNDERFLOW:
646           case EXCEPTION_INT_DIVIDE_BY_ZERO:
647           case EXCEPTION_INT_OVERFLOW:
648             signo = SIGFPE;
649             break;
650           case EXCEPTION_PRIV_INSTRUCTION:
651           case EXCEPTION_ILLEGAL_INSTRUCTION:
652             signo = SIGILL;
653             break;
654           case CONTROL_C_EXIT:
655             signo = SIGINT;
656             break;
657           default:
658             signo = SIGSEGV;
659             break;
660         }
661         waitStatus = WSIGNALED_ENCODE(signo);
662     } else {
663         /* child terminated normally */
664         waitStatus = WEXITED_ENCODE(exitStatus);
665     }
666
667     return waitStatus;
668 }
669
670
671
672 /*
673  * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
674  *     a child process, along with a corresponding buffer read event.
675  *
676  * ASSUMPTIONS: child process is linked with this process management library;
677  *     otherwise no data transfer will take place.
678  */
679 static BOOL
680 CreateChildDataBuffer(DWORD pid,                /* child pid */
681                       void *datap,              /* data to place in buffer */
682                       size_t dataLen,           /* size of data in bytes */
683                       HANDLE *bufMemHandlep,    /* buffer memory handle */
684                       HANDLE *bufEventHandlep)  /* buffer read event handle */
685 {
686     BOOL fsuccess = FALSE;
687     DWORD bufMemSize = dataLen + sizeof(size_t);
688     char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
689     char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
690
691     sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
692     sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
693
694     /* Create and initialize named shared memory and named event */
695
696     *bufMemHandlep =
697         CreateFileMapping((HANDLE)0xFFFFFFFF, /* page-file backed */
698                           NULL,
699                           PAGE_READWRITE,
700                           0,
701                           bufMemSize,
702                           bufMemName);
703
704     if (*bufMemHandlep != NULL) {
705         void *bufMemp;
706
707         bufMemp = MapViewOfFile(*bufMemHandlep,
708                                 FILE_MAP_WRITE, 0, 0, bufMemSize);
709
710         if (bufMemp != NULL) {
711             /* copy data into shared memory, prefixed with data size */
712             size_t *memp = (size_t *)bufMemp;
713
714             *memp++ = dataLen;
715             memcpy((void *)memp, datap, dataLen);
716
717             if (UnmapViewOfFile(bufMemp)) {
718                 /* create buffer read event */
719                 *bufEventHandlep = CreateEvent(NULL,
720                                                FALSE /* manual reset */,
721                                                FALSE /* initial state */,
722                                                bufEventName);
723                 if (*bufEventHandlep != NULL) {
724                     fsuccess = TRUE;
725                 }
726             }
727         }
728
729         if (!fsuccess) {
730             (void) CloseHandle(*bufMemHandlep);
731         }
732     }
733
734     if (!fsuccess) {
735         *bufMemHandlep = *bufEventHandlep = NULL;
736     }
737     return fsuccess;
738 }
739
740
741
742 /*
743  * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
744  *     if any, and place in allocated storage.
745  */
746 static BOOL
747 ReadChildDataBuffer(void **datap,      /* allocated data buffer */
748                     size_t *dataLen)   /* size of data buffer returned */
749 {
750     BOOL fsuccess = FALSE;
751     char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
752     char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
753     HANDLE bufMemHandle, bufEventHandle;
754
755     sprintf(bufMemName, "%s%d",
756             PMGT_DATA_MEM_PREFIX, (int)GetCurrentProcessId());
757     sprintf(bufEventName, "%s%d",
758             PMGT_DATA_EVENT_PREFIX, (int)GetCurrentProcessId());
759
760     /* Attempt to open named event and named shared memory */
761
762     bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
763
764     if (bufEventHandle != NULL) {
765         bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
766
767         if (bufMemHandle != NULL) {
768             void *bufMemp;
769
770             bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
771
772             if (bufMemp != NULL) {
773                 /* read data size and data from shared memory */
774                 size_t *memp = (size_t *)bufMemp;
775
776                 *dataLen = *memp++;
777                 *datap = (void *)malloc(*dataLen);
778
779                 if (*datap != NULL) {
780                     memcpy(*datap, (void *)memp, *dataLen);
781                     fsuccess = TRUE;
782                 }
783                 (void) UnmapViewOfFile(bufMemp);
784             }
785
786             (void) CloseHandle(bufMemHandle);
787         }
788
789         (void) SetEvent(bufEventHandle);
790         (void) CloseHandle(bufEventHandle);
791     }
792
793     if (!fsuccess) {
794         *datap = NULL;
795         *dataLen = 0;
796     }
797     return fsuccess;
798 }
799
800
801
802 /*
803  * ChildMonitorThread() -- Thread spawned to monitor status of child process.
804  *
805  *     Param must be index into child process table.
806  */
807 static DWORD WINAPI
808 ChildMonitorThread(LPVOID param)
809 {
810     int tidx = (int)param;
811     HANDLE childProcHandle;
812     BOOL fsuccess;
813     DWORD rc = -1;
814
815     /* retrieve handle for child process from process table and duplicate */
816
817     (void) pthread_mutex_lock(&procTableLock);
818
819     fsuccess =
820         DuplicateHandle(GetCurrentProcess(),      /* source process handle */
821                         procTable[tidx].p_handle, /* source handle to dup */
822                         GetCurrentProcess(),      /* target process handle */
823                         &childProcHandle,   /* target handle (duplicate) */
824                         0,                  /* access (ignored here) */
825                         FALSE,              /* not inheritable */
826                         DUPLICATE_SAME_ACCESS);
827
828     (void) pthread_mutex_unlock(&procTableLock);
829
830     if (fsuccess) {
831         /* wait for child process to terminate */
832
833         if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
834             /* child process terminated; mark in table and signal event */
835             (void) pthread_mutex_lock(&procTableLock);
836
837             procTable[tidx].p_terminated = TRUE;
838             (void) GetExitCodeProcess(childProcHandle,
839                                       &procTable[tidx].p_status);
840             procTermCount++;
841
842             (void) pthread_mutex_unlock(&procTableLock);
843
844             (void) pthread_cond_broadcast(&childTermEvent);
845
846             /* process/raise SIGCHLD; do last in case handler never returns */
847             ProcessSignal(SIGCHLD);
848             rc = 0;
849         }
850
851         (void) CloseHandle(childProcHandle);
852     }
853
854     /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
855      *       fail; however, this should never happen.
856      */
857     return rc;
858 }
859
860
861
862 /*
863  * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
864  *
865  *     Returns pid of the child process ((pid_t)-1 on failure with errno set).
866  *
867  *     Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
868  *            Open files are not inherited; child's stdin, stdout, and stderr
869  *                are set to parent's console.
870  *            If spath does not specify a filename extension ".exe" is used.
871  *            If sdatap is not NULL, and sdatalen > 0, data is passed to child.
872  *            The spath and sargv[] strings must not contain quote chars (").
873  *
874  * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
875  */
876 pid_t
877 pmgt_ProcessSpawnVEB(const char *spath,
878                      char *sargv[],
879                      char *senvp[],
880                      void *sdatap,
881                      size_t sdatalen)
882 {
883     int tidx;
884     char *pathbuf, *argbuf, *envbuf;
885     char pathext[_MAX_EXT];
886     STARTUPINFO startInfo;
887     PROCESS_INFORMATION procInfo;
888     HANDLE monitorHandle = NULL;
889     HANDLE bufMemHandle, bufEventHandle;
890     DWORD monitorId, createFlags;
891     BOOL passingBuffer = (sdatap != NULL && sdatalen > 0);
892     BOOL fsuccess;
893
894     /* verify arguments */
895     if (!spath || !sargv) {
896         errno = EFAULT;
897         return (pid_t)-1;
898     } else if (*spath == '\0') {
899         errno = ENOENT;
900         return (pid_t)-1;
901     }
902
903     /* create path with .exe extension if no filename extension supplied */
904     if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */))) {
905         errno = ENOMEM;
906         return ((pid_t)-1);
907     }
908     strcpy(pathbuf, spath);
909
910     _splitpath(pathbuf, NULL, NULL, NULL, pathext);
911     if (*pathext == '\0') {
912         /* no filename extension supplied for spath; .exe is assumed */
913         strcat(pathbuf, ".exe");
914     }
915
916     /* create command line argument string */
917     argbuf = StringArrayToString(sargv);
918
919     if (!argbuf) {
920         free(pathbuf);
921         errno = ENOMEM;
922         return ((pid_t)-1);
923     }
924
925     /* create environment variable block (multistring) */
926     if (senvp) {
927         /* use environment variables provided */
928         envbuf = StringArrayToMultiString(senvp);
929
930         if (!envbuf) {
931             free(pathbuf);
932             free(argbuf);
933             errno = ENOMEM;
934             return ((pid_t)-1);
935         }
936     } else {
937         /* use default environment variables */
938         envbuf = NULL;
939     }
940
941     /* set process creation flags */
942     createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
943
944     if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
945         createFlags |= DETACHED_PROCESS;
946     }
947
948     /* clear start-up info; use defaults */
949     memset((void *)&startInfo, 0, sizeof(startInfo));
950     startInfo.cb = sizeof(startInfo);
951
952     /* perform the following as a logically atomic unit:
953      *     1) allocate a process table entry
954      *     2) spawn child process (suspended)
955      *     3) create data buffer to pass (optional)
956      *     4) initialize process table entry
957      *     5) start child watcher thread
958      *     6) resume spawned child process
959      */
960
961     (void) pthread_mutex_lock(&procTableLock);
962
963     for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
964         if (procTable[tidx].p_handle == NULL && 
965             procTable[tidx].p_reserved == FALSE) {
966             procTable[tidx].p_reserved = TRUE;
967             break;
968         }
969     }
970     (void) pthread_mutex_unlock(&procTableLock);
971
972     if (tidx >= PMGT_CHILD_MAX) {
973         /* no space left in process table */
974         free(pathbuf);
975         free(argbuf);
976         free(envbuf);
977
978         errno = EAGAIN;
979         return (pid_t)-1;
980     }
981
982     fsuccess =
983         CreateProcess(pathbuf,     /* executable path */
984                       argbuf,      /* command line argument string */
985                       NULL,        /* default process security attr */
986                       NULL,        /* default thread security attr */
987                       FALSE,       /* do NOT inherit handles */
988                       createFlags, /* creation control flags */
989                       envbuf,      /* environment variable block */
990                       NULL,        /* current directory is that of parent */
991                       &startInfo,  /* startup info block */
992                       &procInfo);
993
994     free(pathbuf);
995     free(argbuf);
996     free(envbuf);
997
998     if (!fsuccess) {
999         /* failed to spawn process */
1000         errno = nterr_nt2unix(GetLastError(), ENOENT);
1001
1002         (void) pthread_mutex_lock(&procTableLock);
1003         procTable[tidx].p_reserved = FALSE;   /* mark entry as not reserved */
1004         (void) pthread_mutex_unlock(&procTableLock);
1005
1006         return (pid_t)-1;
1007     }
1008
1009     if (passingBuffer) {
1010         /* create named data buffer and read event for child */
1011         fsuccess = CreateChildDataBuffer(procInfo.dwProcessId,
1012                                          sdatap,
1013                                          sdatalen,
1014                                          &bufMemHandle,
1015                                          &bufEventHandle);
1016         if (!fsuccess) {
1017             (void) pthread_mutex_lock(&procTableLock);
1018             procTable[tidx].p_reserved = FALSE;  /* mark entry not reserved */
1019             (void) pthread_mutex_unlock(&procTableLock);
1020
1021             (void) TerminateProcess(procInfo.hProcess,
1022                                     PMGT_SIGSTATUS_ENCODE(SIGKILL));
1023             (void) CloseHandle(procInfo.hThread);
1024             (void) CloseHandle(procInfo.hProcess);
1025
1026             errno = EAGAIN;
1027             return (pid_t)-1;
1028         }
1029     }
1030
1031     (void) pthread_mutex_lock(&procTableLock);
1032
1033     procTable[tidx].p_handle     = procInfo.hProcess;
1034     procTable[tidx].p_id         = procInfo.dwProcessId;
1035     procTable[tidx].p_terminated = FALSE;
1036
1037     procEntryCount++;
1038
1039     /* Note: must hold procTableLock during monitor thread creation so
1040      * that if creation fails we can clean up process table before another
1041      * thread has a chance to see this procTable entry.  Continue to hold
1042      * procTableLock while resuming child process, since the procTable
1043      * entry contains a copy of the child process handle which we might use.
1044      */
1045     monitorHandle =
1046         CreateThread(NULL,                  /* default security attr. */
1047                      0,                     /* default stack size */
1048                      ChildMonitorThread,
1049                      (LPVOID)tidx,          /* thread argument */
1050                      0,                     /* creation flags */
1051                      &monitorId);           /* thread id */
1052
1053     if (monitorHandle == NULL) {
1054         /* failed to start child monitor thread */
1055         procTable[tidx].p_handle   = NULL;   /* invalidate table entry */
1056         procTable[tidx].p_reserved = FALSE;  /* mark entry as not reserved */
1057         procEntryCount--;
1058
1059         (void) pthread_mutex_unlock(&procTableLock);
1060
1061         (void) TerminateProcess(procInfo.hProcess,
1062                                 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1063         (void) CloseHandle(procInfo.hThread);
1064         (void) CloseHandle(procInfo.hProcess);
1065
1066         if (passingBuffer) {
1067             (void) CloseHandle(bufMemHandle);
1068             (void) CloseHandle(bufEventHandle);
1069         }
1070
1071         errno = EAGAIN;
1072         return (pid_t)-1;
1073     }
1074
1075     /* Resume child process, which was created suspended to implement spawn
1076      * atomically.  If resumption fails, which it never should, terminate
1077      * the child process with a status of SIGKILL.  Spawn still succeeds and
1078      * the net result is the same as if the child process received a spurious
1079      * SIGKILL signal; the child monitor thread will then handle this.
1080      */
1081     if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1082         (void) TerminateProcess(procInfo.hProcess,
1083                                 PMGT_SIGSTATUS_ENCODE(SIGKILL));
1084
1085         if (passingBuffer) {
1086             /* child will never read data buffer */
1087             (void) SetEvent(bufEventHandle);
1088         }
1089     }
1090
1091     (void) pthread_mutex_unlock(&procTableLock);
1092
1093     (void) CloseHandle(procInfo.hThread);
1094     (void) CloseHandle(monitorHandle);
1095
1096     /* After spawn returns, signals can not be sent to the new child process
1097      * until that child initializes its signal-receiving mechanism (assuming
1098      * the child is linked with this library).  Shorten (but sadly don't
1099      * eliminate) this window of opportunity for failure by yielding this
1100      * thread's time slice.
1101      */
1102     (void) SwitchToThread();
1103
1104     /* If passing a data buffer to child, wait until child reads buffer
1105      * before closing handles and thus freeing resources; if don't wait
1106      * then parent can not safely exit immediately after returning from
1107      * this call (hence why wait is not done in a background thread).
1108      */
1109     if (passingBuffer) {
1110         WaitForSingleObject(bufEventHandle, 10000);
1111         /* note: if wait times out, child may not get to read buffer */
1112         (void) CloseHandle(bufMemHandle);
1113         (void) CloseHandle(bufEventHandle);
1114     }
1115
1116     return (pid_t)procInfo.dwProcessId;
1117 }
1118
1119
1120
1121 /*
1122  * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1123  *     for child to terminate (Unix waitpid() semantics).
1124  *
1125  *     Note: does not support waiting for process in group (i.e., pid
1126  *           equals (pid_t)0 or pid is less than (pid_t)-1.
1127  */
1128 pid_t
1129 pmgt_ProcessWaitPid(pid_t pid,
1130                     int *statusP,
1131                     int options)
1132 {
1133     pid_t rc;
1134     int tidx;
1135     BOOL statusFound = FALSE;
1136     DWORD waitTime;
1137
1138     /* validate arguments */
1139     if (pid < (pid_t)-1 || pid == (pid_t)0) {
1140         errno = EINVAL;
1141         return (pid_t)-1;
1142     }
1143
1144     /* determine how long caller is willing to wait for child */
1145
1146     waitTime = (options & WNOHANG) ? 0 : INFINITE;
1147
1148     /* get child status */
1149
1150     (void) pthread_mutex_lock(&procTableLock);
1151
1152     while (1) {
1153         BOOL waitForChild = FALSE;
1154
1155         if (procEntryCount == 0) {
1156             /* no child processes */
1157             errno = ECHILD;
1158             rc = (pid_t)-1;
1159         } else {
1160             /* determine if status is available for specified child id */
1161
1162             if (pid == (pid_t)-1) {
1163                 /* CASE 1: pid matches any child id */
1164
1165                 if (procTermCount == 0) {
1166                     /* status not available for any child ... */
1167                     if (waitTime == 0) {
1168                         /* ... and caller is not willing to wait */
1169                         rc = (pid_t)0;
1170                     } else {
1171                         /* ... but caller is willing to wait */
1172                         waitForChild = TRUE;
1173                     }
1174                 } else {
1175                     /* status available for some child; locate table entry */
1176                     for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1177                         if (procTable[tidx].p_handle != NULL &&
1178                             procTable[tidx].p_terminated == TRUE) {
1179                             statusFound = TRUE;
1180                             break;
1181                         }
1182                     }
1183
1184                     if (!statusFound) {
1185                         /* should never happen; indicates a bug */
1186                         errno = EINTR;  /* plausible lie for failure */
1187                         rc = (pid_t)-1;
1188                     }
1189                 }
1190
1191             } else {
1192                 /* CASE 2: pid must match a specific child id */
1193
1194                 /* locate table entry */
1195                 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1196                     if (procTable[tidx].p_handle != NULL &&
1197                         procTable[tidx].p_id == (DWORD)pid) {
1198                         break;
1199                     }
1200                 }
1201
1202                 if (tidx >= PMGT_CHILD_MAX) {
1203                     /* pid does not match any child id */
1204                     errno = ECHILD;
1205                     rc = (pid_t)-1;
1206                 } else if (procTable[tidx].p_terminated == FALSE) {
1207                     /* status not available for specified child ... */
1208                     if (waitTime == 0) {
1209                         /* ... and caller is not willing to wait */
1210                         rc = (pid_t)0;
1211                     } else {
1212                         /* ... but caller is willing to wait */
1213                         waitForChild = TRUE;
1214                     }
1215                 } else {
1216                     /* status is available for specified child */
1217                     statusFound = TRUE;
1218                 }
1219             }
1220         }
1221
1222         if (waitForChild) {
1223             (void) pthread_cond_wait(&childTermEvent, &procTableLock);
1224         } else {
1225             break;
1226         }
1227     } /* while() */
1228
1229     if (statusFound) {
1230         /* child status available */
1231         if (statusP) {
1232             *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1233         }
1234         rc = (pid_t)procTable[tidx].p_id;
1235
1236         /* clean up process table */
1237         (void) CloseHandle(procTable[tidx].p_handle);
1238         procTable[tidx].p_handle   = NULL;
1239         procTable[tidx].p_reserved = FALSE;
1240
1241         procEntryCount--;
1242         procTermCount--;
1243     }
1244
1245     (void) pthread_mutex_unlock(&procTableLock);
1246     return rc;
1247 }
1248
1249
1250
1251
1252
1253
1254 /* -----------------  General  ---------------- */
1255
1256
1257
1258 /*
1259  * PmgtLibraryInitialize() -- Initialize process management library.
1260  */
1261 static int
1262 PmgtLibraryInitialize(void)
1263 {
1264     int rc, i;
1265     HANDLE sigPipeHandle;
1266     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1267     HANDLE sigListenerHandle;
1268     DWORD sigListenerId;
1269
1270     /* initialize mutex locks and condition variables */
1271
1272     if ((rc = pthread_mutex_init(&signalTableLock, NULL)) ||
1273         (rc = pthread_mutex_init(&signalBlockLock, NULL)) ||
1274         (rc = pthread_mutex_init(&procTableLock, NULL)) ||
1275         (rc = pthread_cond_init(&childTermEvent, NULL))) {
1276         errno = rc;
1277         return -1;
1278     }
1279
1280     /* initialize signal disposition table */
1281
1282     for (i = 0; i < NSIG; i++) {
1283         if (SignalIsDefined(i)) {
1284             /* initialize to default action for defined signals */
1285             signalTable[i].action.sa_handler = SIG_DFL;
1286             sigemptyset(&signalTable[i].action.sa_mask);
1287             signalTable[i].action.sa_flags = 0;
1288         } else {
1289             /* initialize to ignore for undefined signals */
1290             signalTable[i].action.sa_handler = SIG_IGN;
1291         }
1292     }
1293
1294     /* initialize child process table */
1295
1296     for (i = 0; i < PMGT_CHILD_MAX; i++) {
1297         procTable[i].p_handle = NULL;
1298         procTable[i].p_reserved = FALSE;
1299     }
1300     procEntryCount = 0;
1301     procTermCount = 0;
1302
1303     /* retrieve data buffer passed from parent in spawn, if any */
1304
1305     if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1306         pmgt_spawnData = NULL;
1307         pmgt_spawnDataLen = 0;
1308     }
1309
1310     /* create named pipe for delivering signals to this process */
1311
1312     sprintf(sigPipeName, "%s%d",
1313             PMGT_SIGNAL_PIPE_PREFIX, (int)GetCurrentProcessId());
1314
1315     sigPipeHandle =
1316         CreateNamedPipe(sigPipeName,             /* pipe for this process */
1317                         PIPE_ACCESS_DUPLEX |     /* full duplex pipe */
1318                         WRITE_DAC,               /* DACL write access */
1319                         PIPE_TYPE_MESSAGE |      /* message type pipe */
1320                         PIPE_READMODE_MESSAGE |  /* message read-mode */
1321                         PIPE_WAIT,               /* blocking mode */
1322                         1,                       /* max of 1 pipe instance */
1323                         64,                 /* output buffer size (advisory) */
1324                         64,                 /* input buffer size (advisory) */
1325                         1000,               /* 1 sec default client timeout */
1326                         NULL);              /* default security attr. */
1327
1328     if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1329         /* failed to create signal pipe */
1330         errno = nterr_nt2unix(GetLastError(), EIO);
1331         return -1;
1332     }
1333
1334     /* add entry to signal pipe ACL granting local Administrators R/W access */
1335
1336     (void) ObjectDaclEntryAdd(sigPipeHandle,
1337                               SE_KERNEL_OBJECT,
1338                               LocalAdministratorsGroup,
1339                               GENERIC_READ | GENERIC_WRITE,
1340                               GRANT_ACCESS,
1341                               NO_INHERITANCE);
1342
1343     /* start signal pipe listener thread */
1344
1345     sigListenerHandle =
1346         CreateThread(NULL,                    /* default security attr. */
1347                      0,                       /* default stack size */
1348                      RemoteSignalListenerThread,
1349                      (LPVOID)sigPipeHandle,   /* thread argument */
1350                      0,                       /* creation flags */
1351                      &sigListenerId);         /* thread id */
1352
1353     if (sigListenerHandle != NULL) {
1354         /* listener thread started; bump priority */
1355         (void) SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1356         (void) CloseHandle(sigListenerHandle);
1357     } else {
1358         /* failed to start listener thread */
1359         errno = EAGAIN;
1360         (void) CloseHandle(sigPipeHandle);
1361         return -1;
1362     }
1363
1364     /* redirect native NT signals into this process management library */
1365
1366     if (pmgt_RedirectNativeSignals()) {
1367         /* errno set by called function */
1368         return -1;
1369     }
1370
1371     return 0;
1372 }
1373
1374
1375 /*
1376  * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1377  *     function in the MSVC runtime DLL (msvcrt.dll).
1378  *
1379  *     Note: the system serializes calls to this function.
1380  */
1381 BOOL WINAPI
1382 DllMain(HINSTANCE dllInstHandle,  /* instance handle for this DLL module */
1383         DWORD reason,             /* reason function is being called */
1384         LPVOID reserved)          /* reserved for future use */
1385 {
1386     if (reason == DLL_PROCESS_ATTACH) {
1387         /* library is being attached to a process */
1388         if (PmgtLibraryInitialize()) {
1389             /* failed to initialize library */
1390             return FALSE;
1391         }
1392
1393         /* disable thread attach/detach notifications */
1394         (void) DisableThreadLibraryCalls(dllInstHandle);
1395     }
1396
1397     return TRUE;
1398 }