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