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