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