1 /*
2  * Spawning processes.
3  *
4  * Author:
5  *   Gonzalo Paniagua Javier (gonzalo@novell.com
6  *
7  * (C) 2006 Novell, Inc.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28 #include <config.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 
35 #include <glib.h>
36 
37 #ifdef HAVE_UNISTD_H
38 #ifndef __USE_GNU
39 #define __USE_GNU
40 #endif
41 #include <unistd.h>
42 #endif
43 
44 #ifdef HAVE_SYS_SELECT_H
45 #include <sys/select.h>
46 #endif
47 
48 #ifdef HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #endif
51 
52 #ifdef HAVE_SYS_WAIT_H
53 #include <sys/wait.h>
54 #endif
55 
56 #ifdef HAVE_SYS_RESOURCE_H
57 #  include <sys/resource.h>
58 #endif
59 
60 #ifdef G_OS_WIN32
61 #include <io.h>
62 #include <winsock2.h>
63 #define open _open
64 #define close _close
65 #define read _read
66 #define write _write
67 /* windows pipe api details: http://msdn2.microsoft.com/en-us/library/edze9h7e(VS.80).aspx */
68 #define pipe(x) _pipe(x, 256, 0)
69 #endif
70 
71 #define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
72 #define set_error_cond(cond,msg, ...) do { if ((cond) && error != NULL) *error = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
73 #define set_error_status(status,msg, ...) do { if (error != NULL) *error = g_error_new (G_LOG_DOMAIN, status, msg, __VA_ARGS__); } while (0)
74 #define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR)
75 #define CLOSE_PIPE(p) do { close (p [0]); close (p [1]); } while (0)
76 
77 #if defined(__APPLE__)
78 #if defined (TARGET_OSX)
79 /* Apple defines this in crt_externs.h but doesn't provide that header for
80  * arm-apple-darwin9.  We'll manually define the symbol on Apple as it does
81  * in fact exist on all implementations (so far)
82  */
83 gchar ***_NSGetEnviron(void);
84 #define environ (*_NSGetEnviron())
85 #else
86 static char *mono_environ[1] = { NULL };
87 #define environ mono_environ
88 #endif /* defined (TARGET_OSX) */
89 #elif defined(_MSC_VER)
90 /* MS defines this in stdlib.h */
91 #else
92 extern char **environ;
93 #endif
94 
95 #ifndef G_OS_WIN32
96 static int
safe_read(int fd,gchar * buffer,gint count,GError ** error)97 safe_read (int fd, gchar *buffer, gint count, GError **error)
98 {
99 	int res;
100 
101 	NO_INTR (res, read (fd, buffer, count));
102 	set_error_cond (res == -1, "%s", "Error reading from pipe.");
103 	return res;
104 }
105 
106 static int
read_pipes(int outfd,gchar ** out_str,int errfd,gchar ** err_str,GError ** error)107 read_pipes (int outfd, gchar **out_str, int errfd, gchar **err_str, GError **error)
108 {
109 	fd_set rfds;
110 	int res;
111 	gboolean out_closed;
112 	gboolean err_closed;
113 	GString *out = NULL;
114 	GString *err = NULL;
115 	gchar *buffer = NULL;
116 	gint nread;
117 
118 	out_closed = (outfd < 0);
119 	err_closed = (errfd < 0);
120 	if (out_str) {
121 		*out_str = NULL;
122 		out = g_string_new ("");
123 	}
124 
125 	if (err_str) {
126 		*err_str = NULL;
127 		err = g_string_new ("");
128 	}
129 
130 	do {
131 		if (out_closed && err_closed)
132 			break;
133 
134 #ifdef _MSC_VER
135 #pragma warning(push)
136 #pragma warning(disable:4389)
137 #endif
138 
139 		FD_ZERO (&rfds);
140 		if (!out_closed && outfd >= 0)
141 			FD_SET (outfd, &rfds);
142 		if (!err_closed && errfd >= 0)
143 			FD_SET (errfd, &rfds);
144 
145 #ifdef _MSC_VER
146 #pragma warning(pop)
147 #endif
148 
149 		res = select (MAX (outfd, errfd) + 1, &rfds, NULL, NULL, NULL);
150 		if (res > 0) {
151 			if (buffer == NULL)
152 				buffer = g_malloc (1024);
153 			if (!out_closed && FD_ISSET (outfd, &rfds)) {
154 				nread = safe_read (outfd, buffer, 1024, error);
155 				if (nread < 0) {
156 					close (errfd);
157 					close (outfd);
158 					return -1;
159 				}
160 				g_string_append_len (out, buffer, nread);
161 				if (nread <= 0) {
162 					out_closed = TRUE;
163 					close (outfd);
164 				}
165 			}
166 
167 			if (!err_closed && FD_ISSET (errfd, &rfds)) {
168 				nread = safe_read (errfd, buffer, 1024, error);
169 				if (nread < 0) {
170 					close (errfd);
171 					close (outfd);
172 					return -1;
173 				}
174 				g_string_append_len (err, buffer, nread);
175 				if (nread <= 0) {
176 					err_closed = TRUE;
177 					close (errfd);
178 				}
179 			}
180 		}
181 	} while (res > 0 || (res == -1 && errno == EINTR));
182 
183 	g_free (buffer);
184 	if (out_str)
185 		*out_str = g_string_free (out, FALSE);
186 
187 	if (err_str)
188 		*err_str = g_string_free (err, FALSE);
189 
190 	return 0;
191 }
192 
193 static gboolean
create_pipe(int * fds,GError ** error)194 create_pipe (int *fds, GError **error)
195 {
196 	if (pipe (fds) == -1) {
197 		set_error ("%s", "Error creating pipe.");
198 		return FALSE;
199 	}
200 	return TRUE;
201 }
202 #endif /* G_OS_WIN32 */
203 
204 static int
write_all(int fd,const void * vbuf,size_t n)205 write_all (int fd, const void *vbuf, size_t n)
206 {
207 	const char *buf = (const char *) vbuf;
208 	size_t nwritten = 0;
209 	int w;
210 
211 	do {
212 		do {
213 			w = write (fd, buf + nwritten, n - nwritten);
214 		} while (w == -1 && errno == EINTR);
215 
216 		if (w == -1)
217 			return -1;
218 
219 		nwritten += w;
220 	} while (nwritten < n);
221 
222 	return nwritten;
223 }
224 
225 #ifndef G_OS_WIN32
226 int
eg_getdtablesize(void)227 eg_getdtablesize (void)
228 {
229 #ifdef HAVE_GETRLIMIT
230 	struct rlimit limit;
231 	int res;
232 
233 	res = getrlimit (RLIMIT_NOFILE, &limit);
234 	g_assert (res == 0);
235 	return limit.rlim_cur;
236 #else
237 	return getdtablesize ();
238 #endif
239 }
240 #else
241 int
eg_getdtablesize(void)242 eg_getdtablesize (void)
243 {
244 	g_error ("Should not be called");
245 }
246 #endif
247 
248 gboolean
g_spawn_command_line_sync(const gchar * command_line,gchar ** standard_output,gchar ** standard_error,gint * exit_status,GError ** error)249 g_spawn_command_line_sync (const gchar *command_line,
250 				gchar **standard_output,
251 				gchar **standard_error,
252 				gint *exit_status,
253 				GError **error)
254 {
255 #ifdef G_OS_WIN32
256 #elif !defined (HAVE_FORK) || !defined (HAVE_EXECV)
257 	fprintf (stderr, "g_spawn_command_line_sync not supported on this platform\n");
258 	return FALSE;
259 #else
260 	pid_t pid;
261 	gchar **argv;
262 	gint argc;
263 	int stdout_pipe [2] = { -1, -1 };
264 	int stderr_pipe [2] = { -1, -1 };
265 	int status;
266 	int res;
267 
268 	if (!g_shell_parse_argv (command_line, &argc, &argv, error))
269 		return FALSE;
270 
271 	if (standard_output && !create_pipe (stdout_pipe, error))
272 		return FALSE;
273 
274 	if (standard_error && !create_pipe (stderr_pipe, error)) {
275 		if (standard_output) {
276 			CLOSE_PIPE (stdout_pipe);
277 		}
278 		return FALSE;
279 	}
280 
281 	pid = fork ();
282 	if (pid == 0) {
283 		gint i;
284 
285 		if (standard_output) {
286 			close (stdout_pipe [0]);
287 			dup2 (stdout_pipe [1], STDOUT_FILENO);
288 		}
289 
290 		if (standard_error) {
291 			close (stderr_pipe [0]);
292 			dup2 (stderr_pipe [1], STDERR_FILENO);
293 		}
294 		for (i = eg_getdtablesize () - 1; i >= 3; i--)
295 			close (i);
296 
297 		/* G_SPAWN_SEARCH_PATH is always enabled for g_spawn_command_line_sync */
298 		if (!g_path_is_absolute (argv [0])) {
299 			gchar *arg0;
300 
301 			arg0 = g_find_program_in_path (argv [0]);
302 			if (arg0 == NULL) {
303 				exit (1);
304 			}
305 			//g_free (argv [0]);
306 			argv [0] = arg0;
307 		}
308 		execv (argv [0], argv);
309 		exit (1); /* TODO: What now? */
310 	}
311 
312 	g_strfreev (argv);
313 	if (standard_output)
314 		close (stdout_pipe [1]);
315 
316 	if (standard_error)
317 		close (stderr_pipe [1]);
318 
319 	if (standard_output || standard_error) {
320 		res = read_pipes (stdout_pipe [0], standard_output, stderr_pipe [0], standard_error, error);
321 		if (res) {
322 			waitpid (pid, &status, WNOHANG); /* avoid zombie */
323 			return FALSE;
324 		}
325 	}
326 
327 	NO_INTR (res, waitpid (pid, &status, 0));
328 
329 	/* TODO: What if error? */
330 	if (WIFEXITED (status) && exit_status) {
331 		*exit_status = WEXITSTATUS (status);
332 	}
333 #endif
334 	return TRUE;
335 }
336 
337 /*
338  * This is the only use we have in mono/metadata
339 !g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)
340 */
341 gboolean
g_spawn_async_with_pipes(const gchar * working_directory,gchar ** argv,gchar ** envp,GSpawnFlags flags,GSpawnChildSetupFunc child_setup,gpointer user_data,GPid * child_pid,gint * standard_input,gint * standard_output,gint * standard_error,GError ** error)342 g_spawn_async_with_pipes (const gchar *working_directory,
343 			gchar **argv,
344 			gchar **envp,
345 			GSpawnFlags flags,
346 			GSpawnChildSetupFunc child_setup,
347 			gpointer user_data,
348 			GPid *child_pid,
349 			gint *standard_input,
350 			gint *standard_output,
351 			gint *standard_error,
352 			GError **error)
353 {
354 #ifdef G_OS_WIN32
355 #elif !defined (HAVE_FORK) || !defined (HAVE_EXECVE)
356 	fprintf (stderr, "g_spawn_async_with_pipes is not supported on this platform\n");
357 	return FALSE;
358 #else
359 	pid_t pid;
360 	int info_pipe [2];
361 	int in_pipe [2] = { -1, -1 };
362 	int out_pipe [2] = { -1, -1 };
363 	int err_pipe [2] = { -1, -1 };
364 	int status;
365 
366 	g_return_val_if_fail (argv != NULL, FALSE); /* Only mandatory arg */
367 
368 	if (!create_pipe (info_pipe, error))
369 		return FALSE;
370 
371 	if (standard_output && !create_pipe (out_pipe, error)) {
372 		CLOSE_PIPE (info_pipe);
373 		return FALSE;
374 	}
375 
376 	if (standard_error && !create_pipe (err_pipe, error)) {
377 		CLOSE_PIPE (info_pipe);
378 		CLOSE_PIPE (out_pipe);
379 		return FALSE;
380 	}
381 
382 	if (standard_input && !create_pipe (in_pipe, error)) {
383 		CLOSE_PIPE (info_pipe);
384 		CLOSE_PIPE (out_pipe);
385 		CLOSE_PIPE (err_pipe);
386 		return FALSE;
387 	}
388 
389 	pid = fork ();
390 	if (pid == -1) {
391 		CLOSE_PIPE (info_pipe);
392 		CLOSE_PIPE (out_pipe);
393 		CLOSE_PIPE (err_pipe);
394 		CLOSE_PIPE (in_pipe);
395 		set_error ("%s", "Error in fork ()");
396 		return FALSE;
397 	}
398 
399 	if (pid == 0) {
400 		/* No zombie left behind */
401 		if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
402 			pid = fork ();
403 		}
404 
405 		if (pid != 0) {
406 			exit (pid == -1 ? 1 : 0);
407 		}  else {
408 			gint i;
409 			int fd;
410 			gchar *arg0;
411 			gchar **actual_args;
412 			gint unused;
413 
414 			close (info_pipe [0]);
415 			close (in_pipe [1]);
416 			close (out_pipe [0]);
417 			close (err_pipe [0]);
418 
419 			/* when exec* succeeds, we want to close this fd, which will return
420 			 * a 0 read on the parent. We're not supposed to keep it open forever.
421 			 * If exec fails, we still can write the error to it before closing.
422 			 */
423 			fcntl (info_pipe [1], F_SETFD, FD_CLOEXEC);
424 
425 			if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
426 				pid = getpid ();
427 				NO_INTR (unused, write_all (info_pipe [1], &pid, sizeof (pid_t)));
428 			}
429 
430 			if (working_directory && chdir (working_directory) == -1) {
431 				int err = errno;
432 				NO_INTR (unused, write_all (info_pipe [1], &err, sizeof (int)));
433 				exit (0);
434 			}
435 
436 			if (standard_output) {
437 				dup2 (out_pipe [1], STDOUT_FILENO);
438 			} else if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0) {
439 				fd = open ("/dev/null", O_WRONLY);
440 				dup2 (fd, STDOUT_FILENO);
441 			}
442 
443 			if (standard_error) {
444 				dup2 (err_pipe [1], STDERR_FILENO);
445 			} else if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0) {
446 				fd = open ("/dev/null", O_WRONLY);
447 				dup2 (fd, STDERR_FILENO);
448 			}
449 
450 			if (standard_input) {
451 				dup2 (in_pipe [0], STDIN_FILENO);
452 			} else if ((flags & G_SPAWN_CHILD_INHERITS_STDIN) == 0) {
453 				fd = open ("/dev/null", O_RDONLY);
454 				dup2 (fd, STDIN_FILENO);
455 			}
456 
457 			if ((flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) != 0) {
458 				for (i = eg_getdtablesize () - 1; i >= 3; i--)
459 					close (i);
460 			}
461 
462 			actual_args = ((flags & G_SPAWN_FILE_AND_ARGV_ZERO) == 0) ? argv : argv + 1;
463 			if (envp == NULL)
464 				envp = environ;
465 
466 			if (child_setup)
467 				child_setup (user_data);
468 
469 			arg0 = argv [0];
470 			if (!g_path_is_absolute (arg0) || (flags & G_SPAWN_SEARCH_PATH) != 0) {
471 				arg0 = g_find_program_in_path (argv [0]);
472 				if (arg0 == NULL) {
473 					int err = ENOENT;
474 					write_all (info_pipe [1], &err, sizeof (int));
475 					exit (0);
476 				}
477 			}
478 
479 			execve (arg0, actual_args, envp);
480 			write_all (info_pipe [1], &errno, sizeof (int));
481 			exit (0);
482 		}
483 	} else if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
484 		int w;
485 		/* Wait for the first child if two are created */
486 		NO_INTR (w, waitpid (pid, &status, 0));
487 		if (status == 1 || w == -1) {
488 			CLOSE_PIPE (info_pipe);
489 			CLOSE_PIPE (out_pipe);
490 			CLOSE_PIPE (err_pipe);
491 			CLOSE_PIPE (in_pipe);
492 			set_error ("Error in fork (): %d", status);
493 			return FALSE;
494 		}
495 	}
496 	close (info_pipe [1]);
497 	close (in_pipe [0]);
498 	close (out_pipe [1]);
499 	close (err_pipe [1]);
500 
501 	if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
502 		int x;
503 		NO_INTR (x, read (info_pipe [0], &pid, sizeof (pid_t))); /* if we read < sizeof (pid_t)... */
504 	}
505 
506 	if (child_pid) {
507 		*child_pid = pid;
508 	}
509 
510 	if (read (info_pipe [0], &status, sizeof (int)) != 0) {
511 		close (info_pipe [0]);
512 		close (in_pipe [0]);
513 		close (out_pipe [1]);
514 		close (err_pipe [1]);
515 		set_error_status (status, "Error in exec (%d -> %s)", status, strerror (status));
516 		return FALSE;
517 	}
518 
519 	close (info_pipe [0]);
520 	if (standard_input)
521 		*standard_input = in_pipe [1];
522 	if (standard_output)
523 		*standard_output = out_pipe [0];
524 	if (standard_error)
525 		*standard_error = err_pipe [0];
526 #endif
527 	return TRUE;
528 }
529 
530 
531