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