c7d351b4111cbac714d396d9c271bd10de96f60c
[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
14 #include <afs/stds.h>
15
16 #include <stddef.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <windows.h>
22 #include <pthread.h>
23 #include <afs/errmap_nt.h>
24 #include <afs/secutil_nt.h>
25
26 #include "procmgmt.h"
27 #include "pmgtprivate.h"
28
29
30
31 /* Signal disposition table and associated definitions and locks */
32
33 typedef struct {
34     struct sigaction action;    /* signal action information */
35 } sigtable_entry_t;
36
37 static sigtable_entry_t signalTable[NSIG];      /* signal table; slot 0 unused */
38 static pthread_mutex_t signalTableLock; /* lock protects signalTable */
39
40 /* Global signal block lock; all signal handlers are serialized for now */
41 static pthread_mutex_t signalBlockLock;
42
43 /* Named pipe prefix for sending signals */
44 #define PMGT_SIGNAL_PIPE_PREFIX  "\\\\.\\pipe\\TransarcAfsSignalPipe"
45
46 /* Macro to test process exit status for an uncaught exception */
47 #define PMGT_IS_EXPSTATUS(status)   (((status) & 0xF0000000) == 0xC0000000)
48
49
50
51 /* Child process table and associated definitions and locks */
52
53 typedef struct {
54     HANDLE p_handle;            /* process handle (NULL if table entry not valid) */
55     BOOL p_reserved;            /* table entry is reserved for spawn */
56     DWORD p_id;                 /* process id */
57     BOOL p_terminated;          /* process terminated; status available/valid */
58     DWORD p_status;             /* status of terminated process */
59 } proctable_entry_t;
60
61 /* Max number of active child processes supported */
62 #define PMGT_CHILD_MAX  100
63
64 static proctable_entry_t procTable[PMGT_CHILD_MAX];     /* child process table */
65 static int procEntryCount;      /* count of valid entries in procTable */
66 static int procTermCount;       /* count of terminated entries in procTable */
67
68 /* lock protects procTable, procEntryCount, and procTermCount */
69 static pthread_mutex_t procTableLock;
70
71 /* Named shared memory prefix for passing a data buffer to a child process */
72 #define PMGT_DATA_MEM_PREFIX  "TransarcAfsSpawnDataMemory"
73
74 /* Named event prefix for indicating that a data buffer has been read */
75 #define PMGT_DATA_EVENT_PREFIX  "TransarcAfsSpawnDataEvent"
76
77 /* event signals termination of a child process */
78 static pthread_cond_t childTermEvent;
79
80
81 /* Exported data values */
82
83 void *pmgt_spawnData = NULL;
84 size_t pmgt_spawnDataLen = 0;
85
86
87 /* General definitions */
88
89 #define DWORD_OF_ONES   ((DWORD)0xFFFFFFFF)     /* a common Win32 failure code */
90
91
92
93
94
95
96 /* -----------------  Signals  ---------------- */
97
98
99 /*
100  * SignalIsDefined() -- Determine if an integer value corresponds to a
101  *     signal value defined for this platform.
102  */
103 static int
104 SignalIsDefined(int signo)
105 {
106     int isDefined = 0;
107
108     if (signo >= 1 && signo <= (NSIG - 1)) {
109         /* value is in valid range; check specifics */
110         switch (signo) {
111         case SIGHUP:
112         case SIGINT:
113         case SIGQUIT:
114         case SIGILL:
115         case SIGABRT:
116         case SIGFPE:
117         case SIGKILL:
118         case SIGSEGV:
119         case SIGTERM:
120         case SIGUSR1:
121         case SIGUSR2:
122         case SIGCHLD:
123         case SIGTSTP:
124             isDefined = 1;
125             break;
126         }
127     }
128     return isDefined;
129 }
130
131
132 /*
133  * DefaultActionHandler() -- Execute the default action for the given signal.
134  */
135 static void __cdecl
136 DefaultActionHandler(int signo)
137 {
138     switch (signo) {
139     case SIGHUP:
140     case SIGINT:
141     case SIGKILL:
142     case SIGQUIT:
143     case SIGTERM:
144         /* default action is "exit" */
145         ExitProcess(PMGT_SIGSTATUS_ENCODE(signo));
146         break;
147     case SIGILL:
148     case SIGABRT:
149     case SIGFPE:
150     case SIGSEGV:
151         /* default action is "core" */
152         /* Best we can do is to raise an exception that can be caught by
153          * Dr. Watson, which can in turn generate a crash dump file.
154          * The default exception handler will call ExitProcess() with
155          * our application-specific exception code.
156          */
157         RaiseException((DWORD) PMGT_SIGSTATUS_ENCODE(signo),
158                        EXCEPTION_NONCONTINUABLE, 0, NULL);
159         break;
160     case SIGUSR1:
161     case SIGUSR2:
162     case SIGCHLD:
163         /* default action is "ignore" */
164         break;
165     case SIGTSTP:
166         /* default action is "stop" */
167         /* No good way to implement this from inside a process so ignore */
168         break;
169     default:
170         /* no default action for specified signal value; just ignore */
171         break;
172     }
173 }
174
175
176 /*
177  * ProcessSignal() -- Execute the specified or default handler for the given
178  *     signal; reset the signal's disposition to SIG_DFL if necessary.
179  *     If the signal's disposition is SIG_IGN then no processing takes place.
180  *
181  * ASSUMPTIONS: signo is valid (i.e., SignalIsDefined(signo) is TRUE).
182  */
183 static void
184 ProcessSignal(int signo)
185 {
186     struct sigaction sigEntry;
187
188     if (signo != SIGKILL) {
189         /* serialize signals, but never block processing of SIGKILL */
190         (void)pthread_mutex_lock(&signalBlockLock);
191     }
192
193     /* fetch disposition of signo, updating it if necessary */
194
195     (void)pthread_mutex_lock(&signalTableLock);
196     sigEntry = signalTable[signo].action;
197
198     if ((sigEntry.sa_handler != SIG_IGN) && (sigEntry.sa_flags & SA_RESETHAND)
199         && (signo != SIGILL)) {
200         signalTable[signo].action.sa_handler = SIG_DFL;
201     }
202     (void)pthread_mutex_unlock(&signalTableLock);
203
204     /* execute handler */
205
206     if (sigEntry.sa_handler != SIG_IGN) {
207         if (sigEntry.sa_handler == SIG_DFL) {
208             sigEntry.sa_handler = DefaultActionHandler;
209         }
210         (*sigEntry.sa_handler) (signo);
211     }
212
213     if (signo != SIGKILL) {
214         (void)pthread_mutex_unlock(&signalBlockLock);
215     }
216 }
217
218
219 /*
220  * RemoteSignalThread() -- Thread spawned to process remote signal.
221  *
222  *     Param must be the signal number.
223  */
224 static DWORD WINAPI
225 RemoteSignalThread(LPVOID param)
226 {
227     int signo = (int)(intptr_t)param;
228     DWORD rc = 0;
229
230     if (SignalIsDefined(signo)) {
231         /* process signal */
232         ProcessSignal(signo);
233     } else if (signo != 0) {
234         /* invalid signal value */
235         rc = -1;
236     }
237     return rc;
238 }
239
240
241 /*
242  * RemoteSignalListenerThread() -- Thread spawned to receive and process
243  *     remotely generated signals; never returns.
244  *
245  *     Param must be a handle for a duplex server message pipe in blocking
246  *     mode.
247  */
248 static DWORD WINAPI
249 RemoteSignalListenerThread(LPVOID param)
250 {
251     HANDLE sigPipeHandle = (HANDLE) param;
252     HMODULE hLib = LoadLibrary("AFSPROCMGMT.DLL");
253
254     while (1) {
255         /* wait for pipe client to connect */
256
257         if ((ConnectNamedPipe(sigPipeHandle, NULL))
258             || (GetLastError() == ERROR_PIPE_CONNECTED)) {
259             /* client connected; read signal value */
260             int signo;
261             DWORD bytesXfered;
262
263             if ((ReadFile
264                  (sigPipeHandle, &signo, sizeof(signo), &bytesXfered, NULL))
265                 && (bytesXfered == sizeof(signo))) {
266                 HANDLE sigThreadHandle;
267                 DWORD sigThreadId;
268
269                 /* ACK signal to release sender */
270                 (void)WriteFile(sigPipeHandle, &signo, sizeof(signo),
271                                 &bytesXfered, NULL);
272
273                 /* spawn thread to process signal; we do this so that
274                  * we can always process a SIGKILL even if a signal handler
275                  * invoked earlier fails to return (blocked/spinning).
276                  */
277                 sigThreadHandle = CreateThread(NULL,    /* default security attr. */
278                                                0,       /* default stack size */
279                                                RemoteSignalThread, (LPVOID) (intptr_t)signo,    /* thread argument */
280                                                0,       /* creation flags */
281                                                &sigThreadId);   /* thread id */
282
283                 if (sigThreadHandle != NULL) {
284                     (void)CloseHandle(sigThreadHandle);
285                 }
286             }
287             /* nothing to do if ReadFile, WriteFile or CreateThread fails. */
288
289         } else {
290             /* connect failed; this should never happen */
291             Sleep(2000);        /* sleep 2 seconds to avoid tight loop */
292         }
293
294         (void)DisconnectNamedPipe(sigPipeHandle);
295     }
296
297     /* never reached */
298     FreeLibrary(hLib);
299     return (0);
300 }
301
302
303
304
305
306 /*
307  * pmgt_SigactionSet() -- Examine and/or specify the action for a given
308  *     signal (Unix sigaction() semantics).
309  */
310 int
311 pmgt_SigactionSet(int signo, const struct sigaction *actionP,
312                   struct sigaction *old_actionP)
313 {
314     /* validate arguments */
315
316     if (!SignalIsDefined(signo) || signo == SIGKILL) {
317         /* invalid signal value or signal can't be caught/ignored */
318         errno = EINVAL;
319         return -1;
320     }
321
322     if (actionP && actionP->sa_handler == SIG_ERR) {
323         /* invalid signal disposition */
324         errno = EINVAL;
325         return -1;
326     }
327
328     /* fetch and/or set disposition of signo */
329
330     (void)pthread_mutex_lock(&signalTableLock);
331
332     if (old_actionP) {
333         *old_actionP = signalTable[signo].action;
334     }
335
336     if (actionP) {
337         signalTable[signo].action = *actionP;
338     }
339
340     (void)pthread_mutex_unlock(&signalTableLock);
341
342     return 0;
343 }
344
345
346 /*
347  * pmgt_SignalSet() -- Specify the disposition for a given signal
348  *     value (Unix signal() semantics).
349  */
350 void (__cdecl * pmgt_SignalSet(int signo, void (__cdecl * dispP) (int))) (int) {
351     struct sigaction newAction, oldAction;
352
353     /* construct action to request Unix signal() semantics */
354
355     newAction.sa_handler = dispP;
356     sigemptyset(&newAction.sa_mask);
357     newAction.sa_flags = SA_RESETHAND;
358
359     if (!pmgt_SigactionSet(signo, &newAction, &oldAction)) {
360         /* successfully set new signal action */
361         return oldAction.sa_handler;
362     } else {
363         /* failed to set signal action; errno will have been set */
364         return SIG_ERR;
365     }
366 }
367
368
369 /*
370  * pmgt_SignalRaiseLocal() -- Raise a signal in this process (C raise()
371  *     semantics).
372  */
373 int
374 pmgt_SignalRaiseLocal(int signo)
375 {
376     int rc = 0;
377
378     /* Process signal directly in the context of the calling thread.
379      * This is the same as if the signal had been raised in this process
380      * and this thread chosen to execute the handler.
381      */
382
383     if (SignalIsDefined(signo)) {
384         /* process signal */
385         ProcessSignal(signo);
386     } else if (signo != 0) {
387         /* invalid signal value */
388         errno = EINVAL;
389         rc = -1;
390     }
391     return rc;
392 }
393
394
395 /*
396  * pmgt_SignalRaiseLocalByName() -- Raise a signal in this process where
397  *     the signal is specified by name (C raise() semantics).
398  *
399  *     Upon successful completion, *libSigno is set to the process management
400  *     library's constant value for signame.
401  *
402  *     Note: exists to implement the native-signal redirector (redirect_nt.c),
403  *           which can't include procmgmt.h and hence can't get the SIG* decls.
404  */
405 int
406 pmgt_SignalRaiseLocalByName(const char *signame, int *libSigno)
407 {
408     int rc = 0;
409     int signo;
410
411     if (!strcmp(signame, "SIGHUP")) {
412         signo = SIGHUP;
413     } else if (!strcmp(signame, "SIGINT")) {
414         signo = SIGINT;
415     } else if (!strcmp(signame, "SIGQUIT")) {
416         signo = SIGQUIT;
417     } else if (!strcmp(signame, "SIGILL")) {
418         signo = SIGILL;
419     } else if (!strcmp(signame, "SIGABRT")) {
420         signo = SIGABRT;
421     } else if (!strcmp(signame, "SIGFPE")) {
422         signo = SIGFPE;
423     } else if (!strcmp(signame, "SIGKILL")) {
424         signo = SIGKILL;
425     } else if (!strcmp(signame, "SIGSEGV")) {
426         signo = SIGSEGV;
427     } else if (!strcmp(signame, "SIGTERM")) {
428         signo = SIGTERM;
429     } else if (!strcmp(signame, "SIGUSR1")) {
430         signo = SIGUSR1;
431     } else if (!strcmp(signame, "SIGUSR2")) {
432         signo = SIGUSR2;
433     } else if (!strcmp(signame, "SIGCLD")) {
434         signo = SIGCLD;
435     } else if (!strcmp(signame, "SIGCHLD")) {
436         signo = SIGCHLD;
437     } else if (!strcmp(signame, "SIGTSTP")) {
438         signo = SIGTSTP;
439     } else {
440         /* unknown signal name */
441         errno = EINVAL;
442         rc = -1;
443     }
444
445     if (rc == 0) {
446         *libSigno = signo;
447         rc = pmgt_SignalRaiseLocal(signo);
448     }
449     return rc;
450 }
451
452
453 /*
454  * pmgt_SignalRaiseRemote() -- Raise a signal in the specified process (Unix
455  *     kill() semantics).
456  *
457  *     Note: only supports sending signal to a specific (single) process.
458  */
459 int
460 pmgt_SignalRaiseRemote(pid_t pid, int signo)
461 {
462     BOOL fsuccess;
463     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
464     DWORD ackBytesRead;
465     int signoACK;
466     int status = 0;
467
468     /* validate arguments */
469
470     if ((pid <= (pid_t) 0) || (!SignalIsDefined(signo) && signo != 0)) {
471         /* invalid pid or signo */
472         errno = EINVAL;
473         return -1;
474     }
475
476     /* optimize for the "this process" case */
477
478     if (pid == (pid_t) GetCurrentProcessId()) {
479         return pmgt_SignalRaiseLocal(signo);
480     }
481
482     /* send signal to process via named pipe */
483
484     sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX, (int)pid);
485
486     fsuccess = CallNamedPipe(sigPipeName,       /* process pid's signal pipe */
487                              &signo,    /* data written to pipe */
488                              sizeof(signo),     /* size of data to write */
489                              &signoACK, /* data read from pipe */
490                              sizeof(signoACK),  /* size of data read buffer */
491                              &ackBytesRead,     /* number of bytes actually read */
492                              5 * 1000); /* 5 second timeout */
493
494     if (!fsuccess) {
495         /* failed to send signal via named pipe */
496         status = -1;
497
498         if (signo == SIGKILL) {
499             /* could be a non-AFS process, which might still be kill-able */
500             HANDLE procHandle;
501
502             if (procHandle =
503                 OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) pid)) {
504                 if (TerminateProcess
505                     (procHandle, PMGT_SIGSTATUS_ENCODE(SIGKILL))) {
506                     /* successfully killed process */
507                     status = 0;
508                 } else {
509                     errno = nterr_nt2unix(GetLastError(), EPERM);
510                 }
511                 (void)CloseHandle(procHandle);
512             } else {
513                 if (GetLastError() == ERROR_INVALID_PARAMETER) {
514                     errno = ESRCH;
515                 } else if (GetLastError() == ERROR_ACCESS_DENIED) {
516                     errno = EPERM;
517                 } else {
518                     errno = nterr_nt2unix(GetLastError(), EPERM);
519                 }
520             }
521         } else {
522             /* couldn't open pipe so can't send (non-SIGKILL) signal */
523             errno = nterr_nt2unix(GetLastError(), EPERM);
524         }
525     }
526
527     return status;
528 }
529
530
531
532
533 /* -----------------  Processes  ---------------- */
534
535
536 /*
537  * StringArrayToString() -- convert a null-terminated array of strings,
538  *     such as argv, into a single string of space-separated elements
539  *     with each element quoted (in case it contains space characters
540  *     or is of zero length).
541  */
542 static char *
543 StringArrayToString(char *strArray[])
544 {
545     int strCount = 0;
546     int byteCount = 0;
547     char *buffer = NULL;
548
549     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
550         /* sum all string lengths */
551         byteCount += (int)strlen(strArray[strCount]);
552     }
553
554     /* put all strings into buffer; guarantee buffer is at least one char */
555     buffer = (char *)malloc(byteCount + (strCount * 3) /* quotes+space */ +1);
556     if (buffer != NULL) {
557         int i;
558
559         buffer[0] = '\0';
560
561         for (i = 0; i < strCount; i++) {
562             char *bufp = buffer + strlen(buffer);
563
564             if (i == strCount - 1) {
565                 /* last string; no trailing space */
566                 sprintf(bufp, "\"%s\"", strArray[i]);
567             } else {
568                 sprintf(bufp, "\"%s\" ", strArray[i]);
569             }
570         }
571     }
572
573     return (buffer);
574 }
575
576
577 /*
578  * StringArrayToMultiString() -- convert a null-terminated array of strings,
579  *     such as envp, into a multistring.
580  */
581 static char *
582 StringArrayToMultiString(char *strArray[])
583 {
584     int strCount = 0;
585     int byteCount = 0;
586     char *buffer = NULL;
587
588     for (strCount = 0; strArray[strCount] != NULL; strCount++) {
589         /* sum all string lengths */
590         byteCount += strlen(strArray[strCount]);
591     }
592
593     /* put all strings into buffer; guarantee buffer is at least two chars */
594     buffer = (char *)malloc(byteCount + strCount + 2);
595     if (buffer != NULL) {
596         if (byteCount == 0) {
597             buffer[0] = '\0';
598             buffer[1] = '\0';
599         } else {
600             int i;
601             char *bufp = buffer;
602
603             for (i = 0; i < strCount; i++) {
604                 int strLen = strlen(strArray[i]);
605
606                 if (strLen > 0) {
607                     /* can not embed zero length string in a multistring */
608                     strcpy(bufp, strArray[i]);
609                     bufp += strLen + 1;
610                 }
611             }
612             *bufp = '\0';       /* terminate multistring */
613         }
614     }
615
616     return (buffer);
617 }
618
619
620
621 /*
622  * ComputeWaitStatus() -- Compute an appropriate wait status value from
623  *     a given process termination (exit) code.
624  */
625 static int
626 ComputeWaitStatus(DWORD exitStatus)
627 {
628     int waitStatus;
629
630     if (PMGT_IS_SIGSTATUS(exitStatus)) {
631         /* child terminated due to an unhandled signal */
632         int signo = PMGT_SIGSTATUS_DECODE(exitStatus);
633         waitStatus = WSIGNALED_ENCODE(signo);
634     } else if (PMGT_IS_EXPSTATUS(exitStatus)) {
635         /* child terminated due to an uncaught exception */
636         int signo;
637
638         switch (exitStatus) {
639         case EXCEPTION_FLT_DENORMAL_OPERAND:
640         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
641         case EXCEPTION_FLT_INEXACT_RESULT:
642         case EXCEPTION_FLT_INVALID_OPERATION:
643         case EXCEPTION_FLT_OVERFLOW:
644         case EXCEPTION_FLT_STACK_CHECK:
645         case EXCEPTION_FLT_UNDERFLOW:
646         case EXCEPTION_INT_DIVIDE_BY_ZERO:
647         case EXCEPTION_INT_OVERFLOW:
648             signo = SIGFPE;
649             break;
650         case EXCEPTION_PRIV_INSTRUCTION:
651         case EXCEPTION_ILLEGAL_INSTRUCTION:
652             signo = SIGILL;
653             break;
654         case CONTROL_C_EXIT:
655             signo = SIGINT;
656             break;
657         default:
658             signo = SIGSEGV;
659             break;
660         }
661         waitStatus = WSIGNALED_ENCODE(signo);
662     } else {
663         /* child terminated normally */
664         waitStatus = WEXITED_ENCODE(exitStatus);
665     }
666
667     return waitStatus;
668 }
669
670
671
672 /*
673  * CreateChildDataBuffer() -- Create and fill a named data buffer to pass to
674  *     a child process, along with a corresponding buffer read event.
675  *
676  * ASSUMPTIONS: child process is linked with this process management library;
677  *     otherwise no data transfer will take place.
678  */
679 static BOOL
680 CreateChildDataBuffer(DWORD pid,        /* child pid */
681                       void *datap,      /* data to place in buffer */
682                       size_t dataLen,   /* size of data in bytes */
683                       HANDLE * bufMemHandlep,   /* buffer memory handle */
684                       HANDLE * bufEventHandlep)
685 {                               /* buffer read event handle */
686     BOOL fsuccess = FALSE;
687     DWORD bufMemSize = dataLen + (DWORD)sizeof(size_t);
688     char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
689     char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
690
691     sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX, (int)pid);
692     sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX, (int)pid);
693
694     /* Create and initialize named shared memory and named event */
695
696     *bufMemHandlep = CreateFileMapping(INVALID_HANDLE_VALUE,    /* page-file backed */
697                                        NULL, PAGE_READWRITE, 0, bufMemSize,
698                                        bufMemName);
699
700     if (*bufMemHandlep != NULL) {
701         void *bufMemp;
702
703         bufMemp =
704             MapViewOfFile(*bufMemHandlep, FILE_MAP_WRITE, 0, 0, bufMemSize);
705
706         if (bufMemp != NULL) {
707             /* copy data into shared memory, prefixed with data size */
708             size_t *memp = (size_t *) bufMemp;
709
710             *memp++ = dataLen;
711             memcpy((void *)memp, datap, dataLen);
712
713             if (UnmapViewOfFile(bufMemp)) {
714                 /* create buffer read event */
715                 *bufEventHandlep =
716                     CreateEvent(NULL, FALSE /* manual reset */ ,
717                                 FALSE /* initial state */ ,
718                                 bufEventName);
719                 if (*bufEventHandlep != NULL) {
720                     fsuccess = TRUE;
721                 }
722             }
723         }
724
725         if (!fsuccess) {
726             (void)CloseHandle(*bufMemHandlep);
727         }
728     }
729
730     if (!fsuccess) {
731         *bufMemHandlep = *bufEventHandlep = NULL;
732     }
733     return fsuccess;
734 }
735
736
737
738 /*
739  * ReadChildDataBuffer() -- Read data buffer passed to child from parent,
740  *     if any, and place in allocated storage.
741  */
742 static BOOL
743 ReadChildDataBuffer(void **datap,       /* allocated data buffer */
744                     size_t * dataLen)
745 {                               /* size of data buffer returned */
746     BOOL fsuccess = FALSE;
747     char bufMemName[sizeof(PMGT_DATA_MEM_PREFIX) + 20];
748     char bufEventName[sizeof(PMGT_DATA_EVENT_PREFIX) + 20];
749     HANDLE bufMemHandle, bufEventHandle;
750
751     sprintf(bufMemName, "%s%d", PMGT_DATA_MEM_PREFIX,
752             (int)GetCurrentProcessId());
753     sprintf(bufEventName, "%s%d", PMGT_DATA_EVENT_PREFIX,
754             (int)GetCurrentProcessId());
755
756     /* Attempt to open named event and named shared memory */
757
758     bufEventHandle = OpenEvent(EVENT_MODIFY_STATE, FALSE, bufEventName);
759
760     if (bufEventHandle != NULL) {
761         bufMemHandle = OpenFileMapping(FILE_MAP_READ, FALSE, bufMemName);
762
763         if (bufMemHandle != NULL) {
764             void *bufMemp;
765
766             bufMemp = MapViewOfFile(bufMemHandle, FILE_MAP_READ, 0, 0, 0);
767
768             if (bufMemp != NULL) {
769                 /* read data size and data from shared memory */
770                 size_t *memp = (size_t *) bufMemp;
771
772                 *dataLen = *memp++;
773                 *datap = (void *)malloc(*dataLen);
774
775                 if (*datap != NULL) {
776                     memcpy(*datap, (void *)memp, *dataLen);
777                     fsuccess = TRUE;
778                 }
779                 (void)UnmapViewOfFile(bufMemp);
780             }
781
782             (void)CloseHandle(bufMemHandle);
783         }
784
785         (void)SetEvent(bufEventHandle);
786         (void)CloseHandle(bufEventHandle);
787     }
788
789     if (!fsuccess) {
790         *datap = NULL;
791         *dataLen = 0;
792     }
793     return fsuccess;
794 }
795
796
797
798 /*
799  * ChildMonitorThread() -- Thread spawned to monitor status of child process.
800  *
801  *     Param must be index into child process table.
802  */
803 static DWORD WINAPI
804 ChildMonitorThread(LPVOID param)
805 {
806     int tidx = (int)(intptr_t)param;
807     HANDLE childProcHandle;
808     BOOL fsuccess;
809     DWORD rc = -1;
810
811     /* retrieve handle for child process from process table and duplicate */
812
813     (void)pthread_mutex_lock(&procTableLock);
814
815     fsuccess = DuplicateHandle(GetCurrentProcess(),     /* source process handle */
816                                procTable[tidx].p_handle,        /* source handle to dup */
817                                GetCurrentProcess(),     /* target process handle */
818                                &childProcHandle,        /* target handle (duplicate) */
819                                0,       /* access (ignored here) */
820                                FALSE,   /* not inheritable */
821                                DUPLICATE_SAME_ACCESS);
822
823     (void)pthread_mutex_unlock(&procTableLock);
824
825     if (fsuccess) {
826         /* wait for child process to terminate */
827
828         if (WaitForSingleObject(childProcHandle, INFINITE) == WAIT_OBJECT_0) {
829             /* child process terminated; mark in table and signal event */
830             (void)pthread_mutex_lock(&procTableLock);
831
832             procTable[tidx].p_terminated = TRUE;
833             (void)GetExitCodeProcess(childProcHandle,
834                                      &procTable[tidx].p_status);
835             procTermCount++;
836
837             (void)pthread_mutex_unlock(&procTableLock);
838
839             (void)pthread_cond_broadcast(&childTermEvent);
840
841             /* process/raise SIGCHLD; do last in case handler never returns */
842             ProcessSignal(SIGCHLD);
843             rc = 0;
844         }
845
846         (void)CloseHandle(childProcHandle);
847     }
848
849     /* note: nothing can be done if DuplicateHandle() or WaitForSingleObject()
850      *       fail; however, this should never happen.
851      */
852     return rc;
853 }
854
855
856
857 /*
858  * pmgt_ProcessSpawnVEB() -- Spawn a process (Unix fork()/execve() semantics)
859  *
860  *     Returns pid of the child process ((pid_t)-1 on failure with errno set).
861  *
862  *     Notes: A senvp value of NULL results in Unix fork()/execv() semantics.
863  *            Open files are not inherited; child's stdin, stdout, and stderr
864  *                are set to parent's console.
865  *            If spath does not specify a filename extension ".exe" is used.
866  *            If sdatap is not NULL, and sdatalen > 0, data is passed to child.
867  *            The spath and sargv[] strings must not contain quote chars (").
868  *
869  * ASSUMPTIONS: sargv[0] is the same as spath (or its last component).
870  */
871 pid_t
872 pmgt_ProcessSpawnVEB(const char *spath, char *sargv[], char *senvp[],
873                      void *sdatap, size_t sdatalen)
874 {
875     int tidx;
876     char *pathbuf, *argbuf, *envbuf;
877     char pathext[_MAX_EXT];
878     STARTUPINFO startInfo;
879     PROCESS_INFORMATION procInfo;
880     HANDLE monitorHandle = NULL;
881     HANDLE bufMemHandle, bufEventHandle;
882     DWORD monitorId, createFlags;
883     BOOL passingBuffer = (sdatap != NULL && sdatalen > 0);
884     BOOL fsuccess;
885     int lasterror;
886
887     /* verify arguments */
888     if (!spath || !sargv) {
889         errno = EFAULT;
890         return (pid_t) - 1;
891     } else if (*spath == '\0') {
892         errno = ENOENT;
893         return (pid_t) - 1;
894     }
895
896     /* create path with .exe extension if no filename extension supplied */
897     if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */ ))) {
898         errno = ENOMEM;
899         return ((pid_t) - 1);
900     }
901     strcpy(pathbuf, spath);
902
903     _splitpath(pathbuf, NULL, NULL, NULL, pathext);
904     if (*pathext == '\0') {
905         /* no filename extension supplied for spath; .exe is assumed */
906         strcat(pathbuf, ".exe");
907     }
908
909     /* create command line argument string */
910     argbuf = StringArrayToString(sargv);
911
912     if (!argbuf) {
913         free(pathbuf);
914         errno = ENOMEM;
915         return ((pid_t) - 1);
916     }
917
918     /* create environment variable block (multistring) */
919     if (senvp) {
920         /* use environment variables provided */
921         envbuf = StringArrayToMultiString(senvp);
922
923         if (!envbuf) {
924             free(pathbuf);
925             free(argbuf);
926             errno = ENOMEM;
927             return ((pid_t) - 1);
928         }
929     } else {
930         /* use default environment variables */
931         envbuf = NULL;
932     }
933
934     /* set process creation flags */
935     createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
936
937     if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
938         createFlags |= DETACHED_PROCESS;
939     }
940
941     /* clear start-up info; use defaults */
942     memset((void *)&startInfo, 0, sizeof(startInfo));
943     startInfo.cb = sizeof(startInfo);
944
945     /* perform the following as a logically atomic unit:
946      *     1) allocate a process table entry
947      *     2) spawn child process (suspended)
948      *     3) create data buffer to pass (optional)
949      *     4) initialize process table entry
950      *     5) start child watcher thread
951      *     6) resume spawned child process
952      */
953
954     (void)pthread_mutex_lock(&procTableLock);
955
956     for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
957         if (procTable[tidx].p_handle == NULL
958             && procTable[tidx].p_reserved == FALSE) {
959             procTable[tidx].p_reserved = TRUE;
960             break;
961         }
962     }
963     (void)pthread_mutex_unlock(&procTableLock);
964
965     if (tidx >= PMGT_CHILD_MAX) {
966         /* no space left in process table */
967         free(pathbuf);
968         free(argbuf);
969         free(envbuf);
970
971         errno = EAGAIN;
972         return (pid_t) - 1;
973     }
974
975     fsuccess = CreateProcess(pathbuf,   /* executable path */
976                              argbuf,    /* command line argument string */
977                              NULL,      /* default process security attr */
978                              NULL,      /* default thread security attr */
979                              FALSE,     /* do NOT inherit handles */
980                              createFlags,       /* creation control flags */
981                              envbuf,    /* environment variable block */
982                              NULL,      /* current directory is that of parent */
983                              &startInfo,        /* startup info block */
984                              &procInfo);
985
986     lasterror = GetLastError();
987     free(pathbuf);
988     free(argbuf);
989     free(envbuf);
990
991     if (!fsuccess) {
992         /* failed to spawn process */
993         errno = nterr_nt2unix(lasterror, 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 }