xref: /titanic_41/usr/src/lib/libast/common/misc/procopen.c (revision ead9bb4b1be81d7bbf8ed86ee41d6c1e58b069a3)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24  * Glenn Fowler
25  * AT&T Research
26  *
27  * common process execution support with
28  * proper sfio, signal and wait() syncronization
29  *
30  * _ contains the process path name and is
31  * placed at the top of the environment
32  */
33 
34 #include "proclib.h"
35 
36 #include <ls.h>
37 
38 /*
39  * not quite ready for _use_spawnveg
40  */
41 
42 #if _use_spawnveg && _lib_fork
43 #undef	_use_spawnveg
44 #endif
45 
46 #ifndef DEBUG_PROC
47 #define DEBUG_PROC	1
48 #endif
49 
50 #if _lib_socketpair
51 #if _sys_socket
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #else
55 #undef	_lib_socketpair
56 #endif
57 #endif
58 
59 Proc_t			proc_default = { -1 };
60 
61 #if DEBUG_PROC
62 
63 #include <namval.h>
64 
65 #define PROC_ENV_OPTIONS	"PROC_OPTIONS"
66 
67 #define PROC_OPT_ENVIRONMENT	(1<<0)
68 #define PROC_OPT_EXEC		(1<<1)
69 #define PROC_OPT_TRACE		(1<<2)
70 #define PROC_OPT_VERBOSE	(1<<3)
71 
72 static const Namval_t		options[] =
73 {
74 	"debug",	PROC_OPT_VERBOSE,
75 	"environment",	PROC_OPT_ENVIRONMENT,
76 	"exec",		PROC_OPT_EXEC,
77 	"trace",	PROC_OPT_TRACE,
78 	"verbose",	PROC_OPT_VERBOSE,
79 	0,		0
80 };
81 
82 /*
83  * called by stropt() to set options
84  */
85 
86 static int
87 setopt(register void* a, register const void* p, register int n, const char* v)
88 {
89 	NoP(v);
90 	if (p)
91 	{
92 		if (n)
93 			*((int*)a) |= ((Namval_t*)p)->value;
94 		else
95 			*((int*)a) &= ~((Namval_t*)p)->value;
96 	}
97 	return 0;
98 }
99 
100 #endif
101 
102 #if _use_spawnveg
103 
104 typedef struct Fd_s
105 {
106 	short		fd;
107 	short		flag;
108 } Fd_t;
109 
110 typedef struct Mod_s
111 {
112 	struct Mod_s*	next;
113 	short		op;
114 	short		save;
115 
116 	union
117 	{
118 
119 	struct
120 	{
121 	Fd_t		parent;
122 	Fd_t		child;
123 	}		fd;
124 
125 	Handler_t	handler;
126 
127 	}		arg;
128 
129 } Modify_t;
130 
131 #endif
132 
133 #ifdef SIGPIPE
134 
135 /*
136  * catch but ignore sig
137  * avoids SIG_IGN being passed to children
138  */
139 
140 static void
141 ignoresig(int sig)
142 {
143 	signal(sig, ignoresig);
144 }
145 
146 #endif
147 
148 /*
149  * do modification op and save previous state for restore()
150  */
151 
152 static int
153 modify(Proc_t* proc, int forked, int op, long arg1, long arg2)
154 {
155 #if _lib_fork
156 	if (forked)
157 	{
158 		switch (op)
159 		{
160 		case PROC_fd_dup:
161 		case PROC_fd_dup|PROC_FD_PARENT:
162 		case PROC_fd_dup|PROC_FD_CHILD:
163 		case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
164 			if (arg1 != arg2)
165 			{
166 				if (arg2 != PROC_ARG_NULL)
167 				{
168 					close(arg2);
169 					if (fcntl(arg1, F_DUPFD, arg2) != arg2)
170 						return -1;
171 				}
172 				if (op & PROC_FD_CHILD)
173 					close(arg1);
174 			}
175 			break;
176 		case PROC_sig_dfl:
177 			signal(arg1, SIG_DFL);
178 			break;
179 		case PROC_sig_ign:
180 			signal(arg1, SIG_IGN);
181 			break;
182 		case PROC_sys_pgrp:
183 			if (arg1 < 0)
184 				setsid();
185 			else if (arg1 > 0)
186 			{
187 				if (arg1 == 1)
188 					arg1 = 0;
189 				if (setpgid(0, arg1) < 0 && arg1 && errno == EPERM)
190 					setpgid(0, 0);
191 			}
192 			break;
193 		case PROC_sys_umask:
194 			umask(arg1);
195 			break;
196 		default:
197 			return -1;
198 		}
199 	}
200 #if _use_spawnveg
201 	else
202 #endif
203 #else
204 	NoP(forked);
205 #endif
206 #if _use_spawnveg
207 	{
208 		register Modify_t*	m;
209 
210 		if (!(m = newof(NiL, Modify_t, 1, 0)))
211 			return -1;
212 		m->next = proc->mods;
213 		proc->mods = m;
214 		switch (m->op = op)
215 		{
216 		case PROC_fd_dup:
217 		case PROC_fd_dup|PROC_FD_PARENT:
218 		case PROC_fd_dup|PROC_FD_CHILD:
219 		case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
220 			m->arg.fd.parent.fd = (short)arg1;
221 			m->arg.fd.parent.flag = fcntl(arg1, F_GETFD, 0);
222 			if ((m->arg.fd.child.fd = (short)arg2) != arg1)
223 			{
224 				if (arg2 != PROC_ARG_NULL)
225 				{
226 					m->arg.fd.child.flag = fcntl(arg2, F_GETFD, 0);
227 					if ((m->save = fcntl(arg2, F_DUPFD, 3)) < 0)
228 					{
229 						m->op = 0;
230 						return -1;
231 					}
232 					fcntl(m->save, F_SETFD, FD_CLOEXEC);
233 					close(arg2);
234 					if (fcntl(arg1, F_DUPFD, arg2) != arg2)
235 						return -1;
236 					if (op & PROC_FD_CHILD)
237 						close(arg1);
238 				}
239 				else if (op & PROC_FD_CHILD)
240 				{
241 					if (m->arg.fd.parent.flag)
242 						break;
243 					fcntl(arg1, F_SETFD, FD_CLOEXEC);
244 				}
245 				else if (!m->arg.fd.parent.flag)
246 					break;
247 				else
248 					fcntl(arg1, F_SETFD, 0);
249 				return 0;
250 			}
251 			break;
252 		case PROC_sig_dfl:
253 			if ((m->arg.handler = signal(arg1, SIG_DFL)) == SIG_DFL)
254 				break;
255 			m->save = (short)arg1;
256 			return 0;
257 		case PROC_sig_ign:
258 			if ((m->arg.handler = signal(arg1, SIG_IGN)) == SIG_IGN)
259 				break;
260 			m->save = (short)arg1;
261 			return 0;
262 		case PROC_sys_pgrp:
263 			proc->pgrp = arg1;
264 			break;
265 		case PROC_sys_umask:
266 			if ((m->save = (short)umask(arg1)) == arg1)
267 				break;
268 			return 0;
269 		default:
270 			proc->mods = m->next;
271 			free(m);
272 			return -1;
273 		}
274 		proc->mods = m->next;
275 		free(m);
276 	}
277 #else
278 	NoP(proc);
279 #endif
280 	return 0;
281 }
282 
283 #if _use_spawnveg
284 
285 /*
286  * restore modifications
287  */
288 
289 static void
290 restore(Proc_t* proc)
291 {
292 	register Modify_t*	m;
293 	register Modify_t*	p;
294 	int			oerrno;
295 
296 	NoP(proc);
297 	oerrno = errno;
298 	m = proc->mods;
299 	proc->mods = 0;
300 	while (m)
301 	{
302 		switch (m->op)
303 		{
304 		case PROC_fd_dup:
305 		case PROC_fd_dup|PROC_FD_PARENT:
306 		case PROC_fd_dup|PROC_FD_CHILD:
307 		case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
308 			if (m->op & PROC_FD_PARENT)
309 				close(m->arg.fd.parent.fd);
310 			if (m->arg.fd.child.fd != m->arg.fd.parent.fd && m->arg.fd.child.fd != PROC_ARG_NULL)
311 			{
312 				if (!(m->op & PROC_FD_PARENT))
313 				{
314 					if (m->op & PROC_FD_CHILD)
315 					{
316 						close(m->arg.fd.parent.fd);
317 						fcntl(m->arg.fd.child.fd, F_DUPFD, m->arg.fd.parent.fd);
318 					}
319 					fcntl(m->arg.fd.parent.fd, F_SETFD, m->arg.fd.parent.flag);
320 				}
321 				close(m->arg.fd.child.fd);
322 				fcntl(m->save, F_DUPFD, m->arg.fd.child.fd);
323 				close(m->save);
324 				if (m->arg.fd.child.flag)
325 					fcntl(m->arg.fd.child.fd, F_SETFD, FD_CLOEXEC);
326 			}
327 			else if ((m->op & (PROC_FD_PARENT|PROC_FD_CHILD)) == PROC_FD_CHILD)
328 				fcntl(m->arg.fd.parent.fd, F_SETFD, 0);
329 			break;
330 		case PROC_sig_dfl:
331 		case PROC_sig_ign:
332 			signal(m->save, m->arg.handler);
333 			break;
334 		case PROC_sys_umask:
335 			umask(m->save);
336 			break;
337 		}
338 		p = m;
339 		m = m->next;
340 		free(p);
341 	}
342 	errno = oerrno;
343 }
344 
345 #else
346 
347 #define restore(p)
348 
349 #endif
350 
351 /*
352  * fork and exec or spawn proc(argv) and return a Proc_t handle
353  *
354  * pipe not used when PROC_READ|PROC_WRITE omitted
355  * argv==0 duplicates current process if possible
356  * cmd==0 names the current shell
357  * cmd=="" does error cleanup
358  * envv is the child environment
359  * modv is the child modification vector of PROC_*() ops
360  */
361 
362 Proc_t*
363 procopen(const char* cmd, char** argv, char** envv, long* modv, int flags)
364 {
365 	register Proc_t*	proc = 0;
366 	register int		procfd;
367 	register char**		p;
368 	char**			v;
369 	int			i;
370 	int			forked = 0;
371 	int			signalled = 0;
372 	long			n;
373 	char			path[PATH_MAX];
374 	char			env[PATH_MAX + 2];
375 	int			pio[2];
376 #if !_pipe_rw && !_lib_socketpair
377 	int			poi[2];
378 #endif
379 #if defined(SIGCHLD) && ( _lib_sigprocmask || _lib_sigsetmask )
380 	Sig_mask_t		mask;
381 #endif
382 #if _use_spawnveg
383 	int			newenv = 0;
384 #endif
385 #if DEBUG_PROC
386 	int			debug = PROC_OPT_EXEC;
387 #endif
388 
389 #if _lib_fork
390 	if (!argv && (flags & PROC_OVERLAY))
391 #else
392 	if (!argv)
393 #endif
394 	{
395 		errno = ENOEXEC;
396 		return 0;
397 	}
398 	pio[0] = pio[1] = -1;
399 #if !_pipe_rw && !_lib_socketpair
400 	poi[0] = poi[1] = -1;
401 #endif
402 	if (cmd && (!*cmd || !pathpath(path, cmd, NiL, PATH_REGULAR|PATH_EXECUTE)))
403 		goto bad;
404 	switch (flags & (PROC_READ|PROC_WRITE))
405 	{
406 	case 0:
407 		procfd = -1;
408 		break;
409 	case PROC_READ:
410 		procfd = 1;
411 		break;
412 	case PROC_WRITE:
413 		procfd = 0;
414 		break;
415 	case PROC_READ|PROC_WRITE:
416 		procfd = 2;
417 		break;
418 	}
419 	if (proc_default.pid == -1)
420 		proc = &proc_default;
421 	else if (!(proc = newof(0, Proc_t, 1, 0)))
422 		goto bad;
423 	proc->pid = -1;
424 	proc->pgrp = 0;
425 	proc->rfd = -1;
426 	proc->wfd = -1;
427 	proc->flags = flags;
428 	sfsync(NiL);
429 	if (environ && envv != (char**)environ && (envv || (flags & PROC_PARANOID) || argv && (environ[0][0] != '_' || environ[0][1] != '=')))
430 	{
431 		if (!setenviron(NiL))
432 			goto bad;
433 #if _use_spawnveg
434 		newenv = 1;
435 #endif
436 	}
437 	if (procfd >= 0)
438 	{
439 #if _pipe_rw
440 		if (pipe(pio))
441 			goto bad;
442 #else
443 		if (procfd > 1)
444 		{
445 #if _lib_socketpair
446 			if (socketpair(AF_UNIX, SOCK_STREAM, 0, pio))
447 				goto bad;
448 #else
449 			if (pipe(pio) || pipe(poi))
450 				goto bad;
451 #endif
452 		}
453 		else if (pipe(pio))
454 			goto bad;
455 #endif
456 	}
457 	if (flags & PROC_OVERLAY)
458 	{
459 		proc->pid = 0;
460 		forked = 1;
461 	}
462 #if _use_spawnveg
463 	else if (argv)
464 		proc->pid = 0;
465 #endif
466 #if _lib_fork
467 	else
468 	{
469 		if (!(flags & PROC_FOREGROUND))
470 			sigcritical(SIG_REG_EXEC|SIG_REG_PROC);
471 		else
472 		{
473 			signalled = 1;
474 			proc->sigint = signal(SIGINT, SIG_IGN);
475 			proc->sigquit = signal(SIGQUIT, SIG_IGN);
476 #if defined(SIGCHLD)
477 #if _lib_sigprocmask
478 			sigemptyset(&mask);
479 			sigaddset(&mask, SIGCHLD);
480 			sigprocmask(SIG_BLOCK, &mask, &proc->mask);
481 #else
482 #if _lib_sigsetmask
483 			mask = sigmask(SIGCHLD);
484 			proc->mask = sigblock(mask);
485 #else
486 			proc->sigchld = signal(SIGCHLD, SIG_DFL);
487 #endif
488 #endif
489 #endif
490 		}
491 		proc->pid = fork();
492 		if (!(flags & PROC_FOREGROUND))
493 			sigcritical(0);
494 		else if (!proc->pid)
495 		{
496 			if (proc->sigint != SIG_IGN)
497 			{
498 				proc->sigint = SIG_DFL;
499 				signal(SIGINT, proc->sigint);
500 			}
501 			if (proc->sigquit != SIG_IGN)
502 			{
503 				proc->sigquit = SIG_DFL;
504 				signal(SIGQUIT, proc->sigquit);
505 			}
506 #if defined(SIGCHLD)
507 #if _lib_sigprocmask
508 			sigprocmask(SIG_SETMASK, &proc->mask, NiL);
509 #else
510 #if _lib_sigsetmask
511 			sigsetmask(proc->mask);
512 #else
513 			if (proc->sigchld != SIG_IGN)
514 				signal(SIGCHLD, SIG_DFL);
515 #endif
516 #endif
517 #endif
518 		}
519 		else if (proc->pid == -1)
520 			goto bad;
521 		forked = 1;
522 	}
523 #endif
524 	if (!proc->pid)
525 	{
526 #if _use_spawnveg
527 		char**		oenviron = 0;
528 		char*		oenviron0 = 0;
529 
530 		v = 0;
531 #endif
532 #if DEBUG_PROC
533 		stropt(getenv(PROC_ENV_OPTIONS), options, sizeof(*options), setopt, &debug);
534 #if _lib_fork
535 		if (debug & PROC_OPT_TRACE)
536 		{
537 			if (!fork())
538 			{
539 				sfsprintf(path, sizeof(path), "%d", getppid());
540 				execlp("trace", "trace", "-p", path, NiL);
541 				_exit(EXIT_NOTFOUND);
542 			}
543 			sleep(2);
544 		}
545 #endif
546 #endif
547 		if (flags & PROC_DAEMON)
548 		{
549 #ifdef SIGHUP
550 			modify(proc, forked, PROC_sig_ign, SIGHUP, 0);
551 #endif
552 			modify(proc, forked, PROC_sig_dfl, SIGTERM, 0);
553 #ifdef SIGTSTP
554 			modify(proc, forked, PROC_sig_ign, SIGTSTP, 0);
555 #endif
556 #ifdef SIGTTIN
557 			modify(proc, forked, PROC_sig_ign, SIGTTIN, 0);
558 #endif
559 #ifdef SIGTTOU
560 			modify(proc, forked, PROC_sig_ign, SIGTTOU, 0);
561 #endif
562 		}
563 		if (flags & (PROC_BACKGROUND|PROC_DAEMON))
564 		{
565 			modify(proc, forked, PROC_sig_ign, SIGINT, 0);
566 #ifdef SIGQUIT
567 			modify(proc, forked, PROC_sig_ign, SIGQUIT, 0);
568 #endif
569 		}
570 		if (flags & (PROC_DAEMON|PROC_SESSION))
571 			modify(proc, forked, PROC_sys_pgrp, -1, 0);
572 		if (forked || (flags & PROC_OVERLAY))
573 		{
574 			if ((flags & PROC_PRIVELEGED) && !geteuid())
575 			{
576 				setuid(geteuid());
577 				setgid(getegid());
578 			}
579 			if (flags & (PROC_PARANOID|PROC_GID))
580 				setgid(getgid());
581 			if (flags & (PROC_PARANOID|PROC_UID))
582 				setuid(getuid());
583 		}
584 		if (procfd > 1)
585 		{
586 			if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[0], PROC_ARG_NULL))
587 				goto cleanup;
588 			if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[1], 1))
589 				goto cleanup;
590 #if _pipe_rw || _lib_socketpair
591 			if (modify(proc, forked, PROC_fd_dup, 1, 0))
592 				goto cleanup;
593 #else
594 			if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[0], 0))
595 				goto cleanup;
596 			if (poi[1] != 0 && modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[1], PROC_ARG_NULL))
597 				goto cleanup;
598 #endif
599 		}
600 		else if (procfd >= 0)
601 		{
602 			if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!!procfd], !!procfd))
603 				goto cleanup;
604 			if (pio[!procfd] != !!procfd && modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!procfd], PROC_ARG_NULL))
605 				goto cleanup;
606 		}
607 		if (modv)
608 			for (i = 0; n = modv[i]; i++)
609 				switch (PROC_OP(n))
610 				{
611 				case PROC_fd_dup:
612 				case PROC_fd_dup|PROC_FD_PARENT:
613 				case PROC_fd_dup|PROC_FD_CHILD:
614 				case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
615 					if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), PROC_ARG(n, 2)))
616 						goto cleanup;
617 					break;
618 				default:
619 					if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), 0))
620 						goto cleanup;
621 					break;
622 				}
623 #if _lib_fork
624 		if (forked && (flags & PROC_ENVCLEAR))
625 			environ = 0;
626 #if _use_spawnveg
627 		else
628 #endif
629 #endif
630 #if _use_spawnveg
631 		if (newenv)
632 		{
633 			p = environ;
634 			while (*p++);
635 			if (!(oenviron = (char**)memdup(environ, (p - environ) * sizeof(char*))))
636 				goto cleanup;
637 		}
638 #endif
639 		if (argv && envv != (char**)environ)
640 		{
641 #if _use_spawnveg
642 			if (!newenv && environ[0][0] == '_' && environ[0][1] == '=')
643 				oenviron0 = environ[0];
644 #endif
645 			env[0] = '_';
646 			env[1] = '=';
647 			env[2] = 0;
648 			if (!setenviron(env))
649 				goto cleanup;
650 		}
651 		if ((flags & PROC_PARANOID) && setenv("PATH", astconf("PATH", NiL, NiL), 1))
652 			goto cleanup;
653 		if ((p = envv) && p != (char**)environ)
654 			while (*p)
655 				if (!setenviron(*p++))
656 					goto cleanup;
657 		p = argv;
658 #if _lib_fork
659 		if (forked && !p)
660 			return proc;
661 #endif
662 #if DEBUG_PROC
663 		if (!(debug & PROC_OPT_EXEC) || (debug & PROC_OPT_VERBOSE))
664 		{
665 			if ((debug & PROC_OPT_ENVIRONMENT) && (p = environ))
666 				while (*p)
667 					sfprintf(sfstderr, "%s\n", *p++);
668 			sfprintf(sfstderr, "+ %s", cmd ? path : "sh");
669 			if ((p = argv) && *p)
670 				while (*++p)
671 					sfprintf(sfstderr, " %s", *p);
672 			sfprintf(sfstderr, "\n");
673 sfsync(sfstderr);
674 			if (!(debug & PROC_OPT_EXEC))
675 				_exit(0);
676 			p = argv;
677 		}
678 #endif
679 		if (cmd)
680 		{
681 			strcpy(env + 2, path);
682 			if (forked || (flags & PROC_OVERLAY))
683 				execve(path, p, environ);
684 #if _use_spawnveg
685 			else if ((proc->pid = spawnveg(path, p, environ, proc->pgrp)) != -1)
686 				goto cleanup;
687 #endif
688 			if (errno != ENOEXEC)
689 				goto cleanup;
690 
691 			/*
692 			 * try cmd as a shell script
693 			 */
694 
695 			if (!(flags & PROC_ARGMOD))
696 			{
697 				while (*p++);
698 				if (!(v = newof(0, char*, p - argv + 2, 0)))
699 					goto cleanup;
700 				p = v + 2;
701 				if (*argv)
702 					argv++;
703 				while (*p++ = *argv++);
704 				p = v + 1;
705 			}
706 			*p = path;
707 			*--p = "sh";
708 		}
709 		strcpy(env + 2, (flags & PROC_PARANOID) ? astconf("SH", NiL, NiL) : pathshell());
710 		if (forked || (flags & PROC_OVERLAY))
711 			execve(env + 2, p, environ);
712 #if _use_spawnveg
713 		else
714 			proc->pid = spawnveg(env + 2, p, environ, proc->pgrp);
715 #endif
716 	cleanup:
717 		if (forked)
718 		{
719 			if (!(flags & PROC_OVERLAY))
720 				_exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC);
721 			goto bad;
722 		}
723 #if _use_spawnveg
724 		if (v)
725 			free(v);
726 		if (p = oenviron)
727 		{
728 			environ = 0;
729 			while (*p)
730 				if (!setenviron(*p++))
731 					goto bad;
732 			free(oenviron);
733 		}
734 		else if (oenviron0)
735 			environ[0] = oenviron0;
736 		restore(proc);
737 		if (flags & PROC_OVERLAY)
738 			exit(0);
739 #endif
740 	}
741 	if (proc->pid != -1)
742 	{
743 		if (!forked)
744 		{
745 			if (flags & PROC_FOREGROUND)
746 			{
747 				signalled = 1;
748 				proc->sigint = signal(SIGINT, SIG_IGN);
749 				proc->sigquit = signal(SIGQUIT, SIG_IGN);
750 #if defined(SIGCHLD)
751 #if _lib_sigprocmask
752 				sigemptyset(&mask);
753 				sigaddset(&mask, SIGCHLD);
754 				sigprocmask(SIG_BLOCK, &mask, &proc->mask);
755 #else
756 #if _lib_sigsetmask
757 				mask = sigmask(SIGCHLD);
758 				proc->mask = sigblock(mask);
759 #else
760 				proc->sigchld = signal(SIGCHLD, SIG_DFL);
761 #endif
762 #endif
763 #endif
764 			}
765 		}
766 		else if (modv)
767 			for (i = 0; n = modv[i]; i++)
768 				switch (PROC_OP(n))
769 				{
770 				case PROC_fd_dup|PROC_FD_PARENT:
771 				case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
772 					close(PROC_ARG(n, 1));
773 					break;
774 				case PROC_sys_pgrp:
775 					if (proc->pgrp < 0)
776 						proc->pgrp = proc->pid;
777 					else if (proc->pgrp > 0)
778 					{
779 						if (proc->pgrp == 1)
780 							proc->pgrp = proc->pid;
781 						if (setpgid(proc->pid, proc->pgrp) < 0 && proc->pid != proc->pgrp && errno == EPERM)
782 							setpgid(proc->pid, proc->pid);
783 					}
784 					break;
785 				}
786 		if (procfd >= 0)
787 		{
788 #ifdef SIGPIPE
789 			if ((flags & (PROC_WRITE|PROC_IGNORE)) == (PROC_WRITE|PROC_IGNORE))
790 			{
791 				Handler_t	handler;
792 
793 				if ((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL && handler != ignoresig)
794 					signal(SIGPIPE, handler);
795 			}
796 #endif
797 			switch (procfd)
798 			{
799 			case 0:
800 				proc->wfd = pio[1];
801 				close(pio[0]);
802 				break;
803 			default:
804 #if _pipe_rw || _lib_socketpair
805 				proc->wfd = pio[0];
806 #else
807 				proc->wfd = poi[1];
808 				close(poi[0]);
809 #endif
810 				/*FALLTHROUGH*/
811 			case 1:
812 				proc->rfd = pio[0];
813 				close(pio[1]);
814 				break;
815 			}
816 			if (proc->rfd > 2)
817 				fcntl(proc->rfd, F_SETFD, FD_CLOEXEC);
818 			if (proc->wfd > 2)
819 				fcntl(proc->wfd, F_SETFD, FD_CLOEXEC);
820 		}
821 		if (!proc->pid)
822 			proc->pid = getpid();
823 		return proc;
824 	}
825  bad:
826 	if (signalled)
827 	{
828 		if (proc->sigint != SIG_IGN)
829 			signal(SIGINT, proc->sigint);
830 		if (proc->sigquit != SIG_IGN)
831 			signal(SIGQUIT, proc->sigquit);
832 #if defined(SIGCHLD)
833 #if _lib_sigprocmask
834 		sigprocmask(SIG_SETMASK, &proc->mask, NiL);
835 #else
836 #if _lib_sigsetmask
837 		sigsetmask(proc->mask);
838 #else
839 		if (proc->sigchld != SIG_DFL)
840 			signal(SIGCHLD, proc->sigchld);
841 #endif
842 #endif
843 #endif
844 	}
845 	if ((flags & PROC_CLEANUP) && modv)
846 		for (i = 0; n = modv[i]; i++)
847 			switch (PROC_OP(n))
848 			{
849 			case PROC_fd_dup:
850 			case PROC_fd_dup|PROC_FD_PARENT:
851 			case PROC_fd_dup|PROC_FD_CHILD:
852 			case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD:
853 				if (PROC_ARG(n, 2) != PROC_ARG_NULL)
854 					close(PROC_ARG(n, 1));
855 				break;
856 			}
857 	if (pio[0] >= 0)
858 		close(pio[0]);
859 	if (pio[1] >= 0)
860 		close(pio[1]);
861 #if !_pipe_rw && !_lib_socketpair
862 	if (poi[0] >= 0)
863 		close(poi[0]);
864 	if (poi[1] >= 0)
865 		close(poi[1]);
866 #endif
867 	procfree(proc);
868 	return 0;
869 }
870