1 /* pr.c -- Process creation and tracking support
2  * Created: Sun Jan  7 13:34:08 1996 by faith@dict.org
3  * Copyright 1996, 2002 Rickard E. Faith (faith@dict.org)
4  * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * \section{Process Management Routines}
26  *
27  * \intro The process management routines are designed to facilitate the
28  * creation and management of child processes, coprocesses, and associated
29  * pipelines and I/O.  Some support for daemons and socket connections is
30  * also provided.
31  *
32  */
33 
34 #include "maaP.h"
35 #include <errno.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <sys/param.h>
39 #include <sys/wait.h>
40 
41 typedef struct _pr_Obj {
42 	int pid;
43 } *_pr_Obj;
44 
45 static _pr_Obj _pr_objects = NULL;
46 
47 #if 0
48 static void _pr_check(pr_Object object, const char *function)
49 {
50 	Obj o = (Obj)object;
51 
52 	if (!o) err_internal(function, "object is null");
53 #if MAA_MAGIC
54 	if (o->magic != PR_MAGIC)
55 		err_internal(function,
56 					 "Magic match failed: 0x%08x (should be 0x%08x)",
57 					 o->magic,
58 					 PR_MAGIC);
59 #endif
60 }
61 #endif
62 
63 /* The idea for the max_fd call is from W. Richard Stevens; Advanced
64    Programming in the UNIX Environment (Addison-Wesley Publishing Co.,
65    1992); page 43.  The implementation here, however, is different from
66    that provided by Stevens for his open_max routine. */
67 
max_fd(void)68 static int max_fd(void)
69 {
70 	static int maxFd = 0;
71 
72 	if (maxFd) return maxFd;
73 
74 	if ((maxFd = sysconf(_SC_OPEN_MAX)) > 0) return maxFd;
75 
76 #ifdef NOFILE
77 	return maxFd = NOFILE;	/* Usually in sys/params.h */
78 #else
79 # ifdef _NFILE
80 	return maxFd = _NFILE;
81 # else
82 	return maxFd = 256;		/* best guess */
83 # endif
84 #endif
85 }
86 
_pr_init(void)87 static void _pr_init(void)
88 {
89 	if (!_pr_objects)
90 		_pr_objects = xcalloc(max_fd(), sizeof(struct _pr_Obj));
91 }
92 
_pr_shutdown(void)93 void _pr_shutdown(void)
94 {
95 	int i;
96 
97 	if (_pr_objects) {
98 		for (i = 0; i < max_fd(); i++) {
99 			/* FIXME: blah */;
100 			if (_pr_objects[i].pid) {
101 				kill(_pr_objects[i].pid, SIGKILL); /* FIXME! be gentler. */
102 				pr_wait(_pr_objects[i].pid);
103 				_pr_objects[i].pid = 0;
104 			}
105 		}
106 		xfree(_pr_objects);
107 		_pr_objects = NULL;
108 	}
109 }
110 
pr_open(const char * command,int flags,int * infd,int * outfd,int * errfd)111 int pr_open(const char *command, int flags, int *infd, int *outfd, int *errfd)
112 {
113 	int        pid;
114 	int        fdin[2];
115 	int        fdout[2];
116 	int        fderr[2];
117 	arg_List   list;
118 	int        argc;
119 	char       **argv;
120 	int        null;
121 
122 	_pr_init();
123 
124 	if (flags & ~(PR_USE_STDIN | PR_USE_STDOUT | PR_USE_STDERR
125 				  | PR_CREATE_STDIN | PR_CREATE_STDOUT | PR_CREATE_STDERR
126 				  | PR_STDERR_TO_STDOUT))
127 		err_internal(__func__, "Illegal flags: 0x%08x", flags);
128 	if ((flags & PR_USE_STDIN) && (flags & PR_CREATE_STDIN))
129 		err_internal(__func__, "Cannot both use and create stdin");
130 	if ((flags & PR_USE_STDOUT) && (flags & PR_CREATE_STDOUT))
131 		err_internal(__func__, "Cannot both use and create stdout");
132 	if ((flags & PR_USE_STDERR) && (flags & PR_CREATE_STDERR))
133 		err_internal(__func__, "Cannot both use and create stderr");
134 	if ((flags & PR_STDERR_TO_STDOUT)
135 		&& ((flags & PR_USE_STDERR) || (flags & PR_CREATE_STDERR)))
136 		err_internal(__func__,
137 					 "Cannot use/create stderr when duping to stdout");
138 
139 	list = arg_argify(command, 0);
140 	arg_get_vector(list, &argc, &argv);
141 	PRINTF(MAA_PR,("Execing %s with \"%s\"\n", argv[0], command));
142 
143 	if ((flags & PR_CREATE_STDIN) && pipe(fdin) < 0)
144 		err_fatal_errno(__func__, "Cannot create pipe for stdin");
145 	if ((flags & PR_CREATE_STDOUT) && pipe(fdout) < 0)
146 		err_fatal_errno(__func__, "Cannot create pipe for stdout");
147 	if ((flags & PR_CREATE_STDERR) && pipe(fderr) < 0)
148 		err_fatal_errno(__func__, "Cannot create pipe for stderr");
149 
150 	if ((pid = fork()) < 0)
151 		err_fatal_errno(__func__, "Cannot fork");
152 
153 	if (pid == 0) {		/* child */
154 		int        i;
155 
156 #define CHILD(CREATE,USE,fds,writefd,readfd,fd,FILENO,flag)		\
157 		if (flags & CREATE) {									\
158 			close(fds[writefd]);                                \
159 			dup2(fds[readfd], FILENO);                          \
160 			close(fds[readfd]);                                 \
161 		} else if (flags & USE) {								\
162 			if (fd && *fd) {									\
163 				dup2(*fd, FILENO);								\
164 				close(*fd);										\
165 			} else {											\
166 				if ((null = open("/dev/null", flag)) >= 0) {	\
167 					dup2(null, FILENO);							\
168 					close(null);								\
169 				}												\
170 			}													\
171 		}
172 
173 		CHILD(PR_CREATE_STDIN, PR_USE_STDIN, fdin, 1, 0, infd,
174 			  STDIN_FILENO, O_RDONLY);
175 		CHILD(PR_CREATE_STDOUT, PR_USE_STDOUT, fdout, 0, 1, outfd,
176 			  STDOUT_FILENO, O_WRONLY);
177 		CHILD(PR_CREATE_STDERR, PR_USE_STDERR, fderr, 0, 1, errfd,
178 			  STDERR_FILENO, O_WRONLY);
179 
180 #undef CHILD
181 
182 		if (flags & PR_STDERR_TO_STDOUT)
183 			dup2(STDOUT_FILENO, STDERR_FILENO);
184 
185 		for (i = 0; i < max_fd(); i++)
186 			if (_pr_objects[i].pid > 0) close(i);
187 
188 		execvp(argv[0], argv);
189 		_exit(127);
190 	}
191 	/* parent */
192 #define PARENT(CREATE,USE,fds,readfd,writefd,fd,flag,name)  \
193 	if (flags & CREATE) {									\
194 		close(fds[ readfd ]);                               \
195 		*fd = fds[ writefd ];								\
196 		_pr_objects[ *fd ].pid = pid;						\
197 		PRINTF(MAA_PR,(name " = %d; ",*fd));				\
198 	} else if (flags & USE) {								\
199 		if (fd && *fd) {									\
200 			PRINTF(MAA_PR,(name " = %d*; ",*fd));			\
201 			_pr_objects[ *fd ].pid =0;						\
202 			close(*fd);										\
203 		}													\
204 	}
205 
206 	PARENT(PR_CREATE_STDIN,  PR_USE_STDIN, fdin,  0, 1,
207 		   infd,  "w", "stdin");
208 	PARENT(PR_CREATE_STDOUT, PR_USE_STDOUT, fdout, 1, 0,
209 		   outfd, "r", "stdout");
210 	PARENT(PR_CREATE_STDERR, PR_USE_STDERR, fderr, 1, 0,
211 		   errfd, "r", "stderr");
212 
213 #undef PARENT
214 
215 	PRINTF(MAA_PR,("child pid = %d\n",pid));
216 	arg_destroy(list);
217 
218 	return pid;
219 }
220 
pr_wait(int pid)221 int pr_wait(int pid)
222 {
223 	int exitStatus = 0;
224 	int status;
225 
226 	PRINTF(MAA_PR,("waiting on pid %d\n",pid));
227 
228 	while (waitpid(pid, &status, 0) < 0) {
229 		if (errno != EINTR) {
230 			if (errno == ECHILD) return 0;	/* We've already waited */
231 			/* This is really bad... */
232 			PRINTF(MAA_PR,("waitpid() < 0, errno = %d\n", errno));
233 			perror(__func__);
234 			return -1;
235 		}
236 	}
237 
238 	if (WIFEXITED(status))
239 		exitStatus |= WEXITSTATUS(status);
240 
241 	/* SIGPIPE is ok here, since tar may
242 	   shutdown early.  Anything else is a
243 	   problem.  */
244 	if (WIFSIGNALED(status) && WTERMSIG(status) != SIGPIPE)
245 		exitStatus |= 128 + WTERMSIG(status); /* like bash :-) */
246 
247 	PRINTF(MAA_PR,("Child %d exited with status 0x%04x\n",pid,exitStatus));
248 
249 	return exitStatus;
250 }
251 
pr_close_nowait(int fd)252 int pr_close_nowait(int fd)
253 {
254 	int pid;
255 
256 	if (!_pr_objects)
257 		err_internal(__func__, "No previous call to pr_open()");
258 	if (!(pid = _pr_objects[ fd ].pid))
259 		err_internal(__func__, "File (%d) not created by pr_open()", fd);
260 
261 	_pr_objects[ fd ].pid = 0;
262 
263 	close(fd);
264 	return pid;
265 }
266 
pr_close(int fd)267 int pr_close(int fd)
268 {
269 	int pid = pr_close_nowait(fd);
270 
271 	return pr_wait(pid);
272 }
273 
pr_readwrite(int in,int out,const char * inBuffer,int inLen,char * outBuffer,int outMaxLen)274 int pr_readwrite(int in, int out,
275 				 const char *inBuffer, int inLen,
276 				 char *outBuffer, int outMaxLen)
277 {
278 	long           flags;
279 	const char     *inPt  = inBuffer;
280 	char           *outPt = outBuffer;
281 	int            outLen = 0;
282 	fd_set         rfds, wfds, efds;
283 	struct timeval tv;
284 	int            n;
285 	int            count;
286 	int            retval;
287 	int            status;
288 
289 	if ((flags = fcntl(in, F_GETFL)) < 0)
290 		err_fatal_errno(__func__, "Can't get flags for output stream");
291 #ifdef O_NONBLOCK
292 	flags |= O_NONBLOCK;
293 #else
294 	flags |= FNDELAY;
295 #endif
296 	fcntl(in, F_SETFL, flags);
297 
298 	if ((flags = fcntl(out, F_GETFL)) < 0)
299 		err_fatal_errno(__func__, "Can't get flags for input stream");
300 #ifdef O_NONBLOCK
301 	flags |= O_NONBLOCK;
302 #else
303 	flags |= FNDELAY;
304 #endif
305 	fcntl(out, F_SETFL, flags);
306 
307 	n = max(in, out) + 1;
308 
309 	for (;;) {
310 		tv.tv_sec  = 5;
311 		tv.tv_usec = 0;
312 		FD_ZERO(&rfds);
313 		FD_ZERO(&wfds);
314 		FD_ZERO(&efds);
315 		FD_SET(out, &rfds);
316 		FD_SET(out, &efds);
317 		if (inLen) {
318 			FD_SET(in, &wfds);
319 			FD_SET(in, &efds);
320 		}
321 
322 		switch ((retval = select(n, &rfds, &wfds, &efds, &tv)))
323 		{
324 			case -1: err_fatal_errno(__func__, "Filter failed"); break;
325 				/*       case 0:  err_fatal(__func__, "Filter hung\n");         break; */
326 			default:
327 				if (dbg_test(MAA_PR)) {
328 					printf("select(2) returns %d,"
329 						   " inLen = %d, outLen = %d, outMaxLen = %d\n",
330 						   retval, inLen, outLen, outMaxLen);
331 					if (FD_ISSET(in, &rfds))  printf("  in/read\n");
332 					if (FD_ISSET(out, &rfds)) printf("  out/read\n");
333 					if (FD_ISSET(in, &wfds))  printf("  in/write\n");
334 					if (FD_ISSET(out, &wfds)) printf("  out/write\n");
335 					if (FD_ISSET(in, &efds))  printf("  in/error\n");
336 					if (FD_ISSET(out, &efds)) printf("  out/error\n");
337 				}
338 				if (inLen) {
339 					if ((count = write(in, inPt, inLen)) <= 0) {
340 						if (errno != EAGAIN)
341 							err_fatal_errno(__func__, "Error writing to filter");
342 					} else {
343 						PRINTF(MAA_PR,("  wrote %d\n",count));
344 						inLen -= count;
345 						inPt  += count;
346 						if (!inLen) {
347 							pr_close_nowait(in);
348 							n = out + 1;
349 						}
350 					}
351 				}
352 				if ((count = read(out, outPt, outMaxLen)) <= 0) {
353 					if (!count) {
354 						if (inLen)
355 							err_fatal(__func__,
356 									  "End of output, but input not flushed");
357 						if ((status = pr_close(out)))
358 							err_warning(__func__,
359 										"Filter had non-zero exit status: 0x%x",
360 										status);
361 						return outLen;
362 					} else if (errno != EAGAIN)
363 						err_fatal_errno(__func__, "Error reading from filter");
364 				} else {
365 					PRINTF(MAA_PR,("  read %d\n",count));
366 					outLen    += count;
367 					outPt     += count;
368 					if ((outMaxLen -= count) < 0)
369 						err_fatal(__func__, "Output buffer overflow");
370 				}
371 				break;
372 		}
373 	}
374 }
375 
pr_filter(const char * command,const char * inBuffer,int inLen,char * outBuffer,int outMaxLen)376 int pr_filter(const char *command,
377 			  const char *inBuffer, int inLen,
378 			  char *outBuffer, int outMaxLen)
379 {
380 	int  in, out;
381 
382 	pr_open(command, PR_CREATE_STDIN | PR_CREATE_STDOUT,
383 			&in, &out, NULL);
384 	return pr_readwrite(in, out, inBuffer, inLen, outBuffer, outMaxLen);
385 }
386 
pr_spawn(const char * command)387 int pr_spawn(const char *command)
388 {
389 	arg_List list;
390 	int      argc;
391 	char     **argv;
392 	int      pid;
393 	int      status;
394 	int      exitStatus = 0;
395 
396 	_pr_init();
397 
398 	list = arg_argify(command, 0);
399 	arg_get_vector(list, &argc, &argv);
400 	PRINTF(MAA_PR,("Execing %s with \"%s\"\n", argv[0], command));
401 
402 	if ((pid = fork()) < 0)
403 		err_fatal_errno(__func__, "Cannot fork");
404 
405 	if (pid == 0) {		/* child */
406 		execvp(argv[0], argv);
407 		_exit(127);
408 	}
409 
410 	/* parent */
411 	PRINTF(MAA_PR,("child pid = %d\n",pid));
412 	arg_destroy(list);
413 
414 	PRINTF(MAA_PR,("waiting on pid %d\n",pid));
415 	while (waitpid(pid, &status, 0) < 0) {
416 		if (errno != EINTR) {
417 			if (errno == ECHILD) return 0;	/* We've already waited */
418 			/* This is really bad... */
419 			PRINTF(MAA_PR,("waitpid() < 0, errno = %d\n", errno));
420 			perror(__func__);
421 			return -1;
422 		}
423 	}
424 
425 	if (WIFEXITED(status))
426 		exitStatus |= WEXITSTATUS(status);
427 
428 	/* SIGPIPE is ok here, since tar may
429 	   shutdown early.  Anything else is a
430 	   problem.  */
431 	if (WIFSIGNALED(status) && WTERMSIG(status) != SIGPIPE)
432 		exitStatus |= 128 + WTERMSIG(status); /* like bash :-) */
433 
434 	PRINTF(MAA_PR,("Child %d exited with status 0x%04x\n",pid,exitStatus));
435 
436 	return exitStatus;
437 }
438