Remove the RCSID macro
[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
886     /* verify arguments */
887     if (!spath || !sargv) {
888         errno = EFAULT;
889         return (pid_t) - 1;
890     } else if (*spath == '\0') {
891         errno = ENOENT;
892         return (pid_t) - 1;
893     }
894
895     /* create path with .exe extension if no filename extension supplied */
896     if (!(pathbuf = (char *)malloc(strlen(spath) + 5 /* .exe */ ))) {
897         errno = ENOMEM;
898         return ((pid_t) - 1);
899     }
900     strcpy(pathbuf, spath);
901
902     _splitpath(pathbuf, NULL, NULL, NULL, pathext);
903     if (*pathext == '\0') {
904         /* no filename extension supplied for spath; .exe is assumed */
905         strcat(pathbuf, ".exe");
906     }
907
908     /* create command line argument string */
909     argbuf = StringArrayToString(sargv);
910
911     if (!argbuf) {
912         free(pathbuf);
913         errno = ENOMEM;
914         return ((pid_t) - 1);
915     }
916
917     /* create environment variable block (multistring) */
918     if (senvp) {
919         /* use environment variables provided */
920         envbuf = StringArrayToMultiString(senvp);
921
922         if (!envbuf) {
923             free(pathbuf);
924             free(argbuf);
925             errno = ENOMEM;
926             return ((pid_t) - 1);
927         }
928     } else {
929         /* use default environment variables */
930         envbuf = NULL;
931     }
932
933     /* set process creation flags */
934     createFlags = CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS;
935
936     if (getenv(PMGT_SPAWN_DETACHED_ENV_NAME) != NULL) {
937         createFlags |= DETACHED_PROCESS;
938     }
939
940     /* clear start-up info; use defaults */
941     memset((void *)&startInfo, 0, sizeof(startInfo));
942     startInfo.cb = sizeof(startInfo);
943
944     /* perform the following as a logically atomic unit:
945      *     1) allocate a process table entry
946      *     2) spawn child process (suspended)
947      *     3) create data buffer to pass (optional)
948      *     4) initialize process table entry
949      *     5) start child watcher thread
950      *     6) resume spawned child process
951      */
952
953     (void)pthread_mutex_lock(&procTableLock);
954
955     for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
956         if (procTable[tidx].p_handle == NULL
957             && procTable[tidx].p_reserved == FALSE) {
958             procTable[tidx].p_reserved = TRUE;
959             break;
960         }
961     }
962     (void)pthread_mutex_unlock(&procTableLock);
963
964     if (tidx >= PMGT_CHILD_MAX) {
965         /* no space left in process table */
966         free(pathbuf);
967         free(argbuf);
968         free(envbuf);
969
970         errno = EAGAIN;
971         return (pid_t) - 1;
972     }
973
974     fsuccess = CreateProcess(pathbuf,   /* executable path */
975                              argbuf,    /* command line argument string */
976                              NULL,      /* default process security attr */
977                              NULL,      /* default thread security attr */
978                              FALSE,     /* do NOT inherit handles */
979                              createFlags,       /* creation control flags */
980                              envbuf,    /* environment variable block */
981                              NULL,      /* current directory is that of parent */
982                              &startInfo,        /* startup info block */
983                              &procInfo);
984
985     free(pathbuf);
986     free(argbuf);
987     free(envbuf);
988
989     if (!fsuccess) {
990         /* failed to spawn process */
991         errno = nterr_nt2unix(GetLastError(), ENOENT);
992
993         (void)pthread_mutex_lock(&procTableLock);
994         procTable[tidx].p_reserved = FALSE;     /* mark entry as not reserved */
995         (void)pthread_mutex_unlock(&procTableLock);
996
997         return (pid_t) - 1;
998     }
999
1000     if (passingBuffer) {
1001         /* create named data buffer and read event for child */
1002         fsuccess =
1003             CreateChildDataBuffer(procInfo.dwProcessId, sdatap, sdatalen,
1004                                   &bufMemHandle, &bufEventHandle);
1005         if (!fsuccess) {
1006             (void)pthread_mutex_lock(&procTableLock);
1007             procTable[tidx].p_reserved = FALSE; /* mark entry not reserved */
1008             (void)pthread_mutex_unlock(&procTableLock);
1009
1010             (void)TerminateProcess(procInfo.hProcess,
1011                                    PMGT_SIGSTATUS_ENCODE(SIGKILL));
1012             (void)CloseHandle(procInfo.hThread);
1013             (void)CloseHandle(procInfo.hProcess);
1014
1015             errno = EAGAIN;
1016             return (pid_t) - 1;
1017         }
1018     }
1019
1020     (void)pthread_mutex_lock(&procTableLock);
1021
1022     procTable[tidx].p_handle = procInfo.hProcess;
1023     procTable[tidx].p_id = procInfo.dwProcessId;
1024     procTable[tidx].p_terminated = FALSE;
1025
1026     procEntryCount++;
1027
1028     /* Note: must hold procTableLock during monitor thread creation so
1029      * that if creation fails we can clean up process table before another
1030      * thread has a chance to see this procTable entry.  Continue to hold
1031      * procTableLock while resuming child process, since the procTable
1032      * entry contains a copy of the child process handle which we might use.
1033      */
1034     monitorHandle = CreateThread(NULL,  /* default security attr. */
1035                                  0,     /* default stack size */
1036                                  ChildMonitorThread, (LPVOID)(intptr_t) tidx,   /* thread argument */
1037                                  0,     /* creation flags */
1038                                  &monitorId);   /* thread id */
1039
1040     if (monitorHandle == NULL) {
1041         /* failed to start child monitor thread */
1042         procTable[tidx].p_handle = NULL;        /* invalidate table entry */
1043         procTable[tidx].p_reserved = FALSE;     /* mark entry as not reserved */
1044         procEntryCount--;
1045
1046         (void)pthread_mutex_unlock(&procTableLock);
1047
1048         (void)TerminateProcess(procInfo.hProcess,
1049                                PMGT_SIGSTATUS_ENCODE(SIGKILL));
1050         (void)CloseHandle(procInfo.hThread);
1051         (void)CloseHandle(procInfo.hProcess);
1052
1053         if (passingBuffer) {
1054             (void)CloseHandle(bufMemHandle);
1055             (void)CloseHandle(bufEventHandle);
1056         }
1057
1058         errno = EAGAIN;
1059         return (pid_t) - 1;
1060     }
1061
1062     /* Resume child process, which was created suspended to implement spawn
1063      * atomically.  If resumption fails, which it never should, terminate
1064      * the child process with a status of SIGKILL.  Spawn still succeeds and
1065      * the net result is the same as if the child process received a spurious
1066      * SIGKILL signal; the child monitor thread will then handle this.
1067      */
1068     if (ResumeThread(procInfo.hThread) == DWORD_OF_ONES) {
1069         (void)TerminateProcess(procInfo.hProcess,
1070                                PMGT_SIGSTATUS_ENCODE(SIGKILL));
1071
1072         if (passingBuffer) {
1073             /* child will never read data buffer */
1074             (void)SetEvent(bufEventHandle);
1075         }
1076     }
1077
1078     (void)pthread_mutex_unlock(&procTableLock);
1079
1080     (void)CloseHandle(procInfo.hThread);
1081     (void)CloseHandle(monitorHandle);
1082
1083     /* After spawn returns, signals can not be sent to the new child process
1084      * until that child initializes its signal-receiving mechanism (assuming
1085      * the child is linked with this library).  Shorten (but sadly don't
1086      * eliminate) this window of opportunity for failure by yielding this
1087      * thread's time slice.
1088      */
1089     (void)SwitchToThread();
1090
1091     /* If passing a data buffer to child, wait until child reads buffer
1092      * before closing handles and thus freeing resources; if don't wait
1093      * then parent can not safely exit immediately after returning from
1094      * this call (hence why wait is not done in a background thread).
1095      */
1096     if (passingBuffer) {
1097         WaitForSingleObject(bufEventHandle, 10000);
1098         /* note: if wait times out, child may not get to read buffer */
1099         (void)CloseHandle(bufMemHandle);
1100         (void)CloseHandle(bufEventHandle);
1101     }
1102
1103     return (pid_t) procInfo.dwProcessId;
1104 }
1105
1106
1107
1108 /*
1109  * pmgt_ProcessWaitPid() -- Wait for child process status; i.e., wait
1110  *     for child to terminate (Unix waitpid() semantics).
1111  *
1112  *     Note: does not support waiting for process in group (i.e., pid
1113  *           equals (pid_t)0 or pid is less than (pid_t)-1.
1114  */
1115 pid_t
1116 pmgt_ProcessWaitPid(pid_t pid, int *statusP, int options)
1117 {
1118     pid_t rc;
1119     int tidx;
1120     BOOL statusFound = FALSE;
1121     DWORD waitTime;
1122
1123     /* validate arguments */
1124     if (pid < (pid_t) - 1 || pid == (pid_t) 0) {
1125         errno = EINVAL;
1126         return (pid_t) - 1;
1127     }
1128
1129     /* determine how long caller is willing to wait for child */
1130
1131     waitTime = (options & WNOHANG) ? 0 : INFINITE;
1132
1133     /* get child status */
1134
1135     (void)pthread_mutex_lock(&procTableLock);
1136
1137     while (1) {
1138         BOOL waitForChild = FALSE;
1139
1140         if (procEntryCount == 0) {
1141             /* no child processes */
1142             errno = ECHILD;
1143             rc = (pid_t) - 1;
1144         } else {
1145             /* determine if status is available for specified child id */
1146
1147             if (pid == (pid_t) - 1) {
1148                 /* CASE 1: pid matches any child id */
1149
1150                 if (procTermCount == 0) {
1151                     /* status not available for any child ... */
1152                     if (waitTime == 0) {
1153                         /* ... and caller is not willing to wait */
1154                         rc = (pid_t) 0;
1155                     } else {
1156                         /* ... but caller is willing to wait */
1157                         waitForChild = TRUE;
1158                     }
1159                 } else {
1160                     /* status available for some child; locate table entry */
1161                     for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1162                         if (procTable[tidx].p_handle != NULL
1163                             && procTable[tidx].p_terminated == TRUE) {
1164                             statusFound = TRUE;
1165                             break;
1166                         }
1167                     }
1168
1169                     if (!statusFound) {
1170                         /* should never happen; indicates a bug */
1171                         errno = EINTR;  /* plausible lie for failure */
1172                         rc = (pid_t) - 1;
1173                     }
1174                 }
1175
1176             } else {
1177                 /* CASE 2: pid must match a specific child id */
1178
1179                 /* locate table entry */
1180                 for (tidx = 0; tidx < PMGT_CHILD_MAX; tidx++) {
1181                     if (procTable[tidx].p_handle != NULL
1182                         && procTable[tidx].p_id == (DWORD) pid) {
1183                         break;
1184                     }
1185                 }
1186
1187                 if (tidx >= PMGT_CHILD_MAX) {
1188                     /* pid does not match any child id */
1189                     errno = ECHILD;
1190                     rc = (pid_t) - 1;
1191                 } else if (procTable[tidx].p_terminated == FALSE) {
1192                     /* status not available for specified child ... */
1193                     if (waitTime == 0) {
1194                         /* ... and caller is not willing to wait */
1195                         rc = (pid_t) 0;
1196                     } else {
1197                         /* ... but caller is willing to wait */
1198                         waitForChild = TRUE;
1199                     }
1200                 } else {
1201                     /* status is available for specified child */
1202                     statusFound = TRUE;
1203                 }
1204             }
1205         }
1206
1207         if (waitForChild) {
1208             (void)pthread_cond_wait(&childTermEvent, &procTableLock);
1209         } else {
1210             break;
1211         }
1212     }                           /* while() */
1213
1214     if (statusFound) {
1215         /* child status available */
1216         if (statusP) {
1217             *statusP = ComputeWaitStatus(procTable[tidx].p_status);
1218         }
1219         rc = (pid_t) procTable[tidx].p_id;
1220
1221         /* clean up process table */
1222         (void)CloseHandle(procTable[tidx].p_handle);
1223         procTable[tidx].p_handle = NULL;
1224         procTable[tidx].p_reserved = FALSE;
1225
1226         procEntryCount--;
1227         procTermCount--;
1228     }
1229
1230     (void)pthread_mutex_unlock(&procTableLock);
1231     return rc;
1232 }
1233
1234
1235
1236
1237
1238
1239 /* -----------------  General  ---------------- */
1240
1241
1242
1243 /*
1244  * PmgtLibraryInitialize() -- Initialize process management library.
1245  */
1246 static int
1247 PmgtLibraryInitialize(void)
1248 {
1249     int rc, i;
1250     HANDLE sigPipeHandle;
1251     char sigPipeName[sizeof(PMGT_SIGNAL_PIPE_PREFIX) + 20];
1252     HANDLE sigListenerHandle;
1253     DWORD sigListenerId;
1254
1255     /* initialize mutex locks and condition variables */
1256
1257     if ((rc = pthread_mutex_init(&signalTableLock, NULL))
1258         || (rc = pthread_mutex_init(&signalBlockLock, NULL))
1259         || (rc = pthread_mutex_init(&procTableLock, NULL))
1260         || (rc = pthread_cond_init(&childTermEvent, NULL))) {
1261         errno = rc;
1262         return -1;
1263     }
1264
1265     /* initialize signal disposition table */
1266
1267     for (i = 0; i < NSIG; i++) {
1268         if (SignalIsDefined(i)) {
1269             /* initialize to default action for defined signals */
1270             signalTable[i].action.sa_handler = SIG_DFL;
1271             sigemptyset(&signalTable[i].action.sa_mask);
1272             signalTable[i].action.sa_flags = 0;
1273         } else {
1274             /* initialize to ignore for undefined signals */
1275             signalTable[i].action.sa_handler = SIG_IGN;
1276         }
1277     }
1278
1279     /* initialize child process table */
1280
1281     for (i = 0; i < PMGT_CHILD_MAX; i++) {
1282         procTable[i].p_handle = NULL;
1283         procTable[i].p_reserved = FALSE;
1284     }
1285     procEntryCount = 0;
1286     procTermCount = 0;
1287
1288     /* retrieve data buffer passed from parent in spawn, if any */
1289
1290     if (!ReadChildDataBuffer(&pmgt_spawnData, &pmgt_spawnDataLen)) {
1291         pmgt_spawnData = NULL;
1292         pmgt_spawnDataLen = 0;
1293     }
1294
1295     /* create named pipe for delivering signals to this process */
1296
1297     sprintf(sigPipeName, "%s%d", PMGT_SIGNAL_PIPE_PREFIX,
1298             (int)GetCurrentProcessId());
1299
1300     sigPipeHandle = CreateNamedPipe(sigPipeName,        /* pipe for this process */
1301                                     PIPE_ACCESS_DUPLEX |        /* full duplex pipe */
1302                                     WRITE_DAC,  /* DACL write access */
1303                                     PIPE_TYPE_MESSAGE | /* message type pipe */
1304                                     PIPE_READMODE_MESSAGE |     /* message read-mode */
1305                                     PIPE_WAIT,  /* blocking mode */
1306                                     1,  /* max of 1 pipe instance */
1307                                     64, /* output buffer size (advisory) */
1308                                     64, /* input buffer size (advisory) */
1309                                     1000,       /* 1 sec default client timeout */
1310                                     NULL);      /* default security attr. */
1311
1312     if (sigPipeHandle == INVALID_HANDLE_VALUE) {
1313         /* failed to create signal pipe */
1314         errno = nterr_nt2unix(GetLastError(), EIO);
1315         return -1;
1316     }
1317
1318     /* add entry to signal pipe ACL granting local Administrators R/W access */
1319
1320     (void)ObjectDaclEntryAdd(sigPipeHandle, SE_KERNEL_OBJECT,
1321                              LocalAdministratorsGroup,
1322                              GENERIC_READ | GENERIC_WRITE, GRANT_ACCESS,
1323                              NO_INHERITANCE);
1324
1325     /* start signal pipe listener thread */
1326
1327     sigListenerHandle = CreateThread(NULL,      /* default security attr. */
1328                                      0, /* default stack size */
1329                                      RemoteSignalListenerThread, (LPVOID) sigPipeHandle,        /* thread argument */
1330                                      0, /* creation flags */
1331                                      &sigListenerId);   /* thread id */
1332
1333     if (sigListenerHandle != NULL) {
1334         /* listener thread started; bump priority */
1335         (void)SetThreadPriority(sigListenerHandle, THREAD_PRIORITY_HIGHEST);
1336         (void)CloseHandle(sigListenerHandle);
1337     } else {
1338         /* failed to start listener thread */
1339         errno = EAGAIN;
1340         (void)CloseHandle(sigPipeHandle);
1341         return -1;
1342     }
1343
1344     /* redirect native NT signals into this process management library */
1345
1346     if (pmgt_RedirectNativeSignals()) {
1347         /* errno set by called function */
1348         return -1;
1349     }
1350
1351     return 0;
1352 }
1353
1354
1355 /*
1356  * DllMain() -- Entry-point function called by the DllMainCRTStartup()
1357  *     function in the MSVC runtime DLL (msvcrt.dll).
1358  *
1359  *     Note: the system serializes calls to this function.
1360  */
1361 BOOL WINAPI
1362 DllMain(HINSTANCE dllInstHandle,        /* instance handle for this DLL module */
1363         DWORD reason,           /* reason function is being called */
1364         LPVOID reserved)
1365 {                               /* reserved for future use */
1366     switch (reason) {
1367     case DLL_PROCESS_ATTACH:
1368         /* library is being attached to a process */
1369         if (PmgtLibraryInitialize()) {
1370             /* failed to initialize library */
1371             return FALSE;
1372         }
1373
1374         /* disable thread attach/detach notifications */
1375         (void)DisableThreadLibraryCalls(dllInstHandle);
1376         return TRUE;
1377     case DLL_PROCESS_DETACH:
1378         pmgt_RestoreNativeSignals();
1379         return TRUE;
1380     default:
1381         return FALSE;
1382     }
1383 }