/* gspawn.c - Process launching * * Copyright 2000 Red Hat, Inc. * g_execvpe implementation based on GNU libc execvp: * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc. * * GLib is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * GLib is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GLib; see the file COPYING.LIB. If not, write * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "glib.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ #include "glibintl.h" static gint g_execute (const gchar *file, gchar **argv, gchar **envp, gboolean search_path); static gboolean make_pipe (gint p[2], GError **error); static gboolean fork_exec_with_pipes (gboolean intermediate_child, const gchar *working_directory, gchar **argv, gchar **envp, gboolean close_descriptors, gboolean search_path, gboolean stdout_to_null, gboolean stderr_to_null, gboolean child_inherits_stdin, GSpawnChildSetupFunc child_setup, gpointer user_data, gint *child_pid, gint *standard_input, gint *standard_output, gint *standard_error, GError **error); GQuark g_spawn_error_quark (void) { static GQuark quark = 0; if (quark == 0) quark = g_quark_from_static_string ("g-exec-error-quark"); return quark; } /** * g_spawn_async: * @working_directory: child's current working directory, or NULL to inherit parent's * @argv: child's argument vector * @envp: child's environment, or NULL to inherit parent's * @flags: flags from #GSpawnFlags * @child_setup: function to run in the child just before exec() * @user_data: user data for @child_setup * @child_pid: return location for child process ID, or NULL * @error: return location for error * * See g_spawn_async_with_pipes() for a full description; this function * simply calls the g_spawn_async_with_pipes() without any pipes. * * Return value: TRUE on success, FALSE if error is set **/ gboolean g_spawn_async (const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, gint *child_pid, GError **error) { g_return_val_if_fail (argv != NULL, FALSE); return g_spawn_async_with_pipes (working_directory, argv, envp, flags, child_setup, user_data, child_pid, NULL, NULL, NULL, error); } /* Avoids a danger in threaded situations (calling close() * on a file descriptor twice, and another thread has * re-opened it since the first close) */ static gint close_and_invalidate (gint *fd) { gint ret; ret = close (*fd); *fd = -1; return ret; } typedef enum { READ_FAILED = 0, /* FALSE */ READ_OK, READ_EOF } ReadResult; static ReadResult read_data (GString *str, gint fd, GError **error) { gint bytes; gchar buf[4096]; again: bytes = read (fd, &buf, 4096); if (bytes == 0) return READ_EOF; else if (bytes > 0) { g_string_append_len (str, buf, bytes); return READ_OK; } else if (bytes < 0 && errno == EINTR) goto again; else if (bytes < 0) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, _("Failed to read data from child process (%s)"), g_strerror (errno)); return READ_FAILED; } else return READ_OK; } /** * g_spawn_sync: * @working_directory: child's current working directory, or NULL to inherit parent's * @argv: child's argument vector * @envp: child's environment, or NULL to inherit parent's * @flags: flags from #GSpawnFlags * @child_setup: function to run in the child just before exec() * @user_data: user data for @child_setup * @standard_output: return location for child output * @standard_error: return location for child error messages * @exit_status: child exit status, as returned by waitpid() * @error: return location for error * * Executes a child synchronously (waits for the child to exit before returning). * All output from the child is stored in @standard_output and @standard_error, * if those parameters are non-NULL. If @exit_status is non-NULL, the exit status * of the child is stored there as it would be by waitpid(); standard UNIX * macros such as WIFEXITED() and WEXITSTATUS() must be used to evaluate the * exit status. If an error occurs, no data is returned in @standard_output, * @standard_error, or @exit_status. * * This function calls g_spawn_async_with_pipes() internally; see that function * for full details on the other parameters. * * Return value: TRUE on success, FALSE if an error was set. **/ gboolean g_spawn_sync (const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, gchar **standard_output, gchar **standard_error, gint *exit_status, GError **error) { gint outpipe = -1; gint errpipe = -1; gint pid; fd_set fds; gint ret; GString *outstr = NULL; GString *errstr = NULL; gboolean failed; gint status; g_return_val_if_fail (argv != NULL, FALSE); g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE); g_return_val_if_fail (standard_output == NULL || !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE); g_return_val_if_fail (standard_error == NULL || !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE); /* Just to ensure segfaults if callers try to use * these when an error is reported. */ if (standard_output) *standard_output = NULL; if (standard_error) *standard_error = NULL; if (!fork_exec_with_pipes (FALSE, working_directory, argv, envp, !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN), (flags & G_SPAWN_SEARCH_PATH) != 0, (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0, (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0, (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0, child_setup, user_data, &pid, NULL, standard_output ? &outpipe : NULL, standard_error ? &errpipe : NULL, error)) return FALSE; /* Read data from child. */ failed = FALSE; if (outpipe >= 0) { outstr = g_string_new (""); } if (errpipe >= 0) { errstr = g_string_new (""); } /* Read data until we get EOF on both pipes. */ while (!failed && (outpipe >= 0 || errpipe >= 0)) { ret = 0; FD_ZERO (&fds); if (outpipe >= 0) FD_SET (outpipe, &fds); if (errpipe >= 0) FD_SET (errpipe, &fds); ret = select (MAX (outpipe, errpipe) + 1, &fds, NULL, NULL, NULL /* no timeout */); if (ret < 0 && errno != EINTR) { failed = TRUE; g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, _("Unexpected error in select() reading data from a child process (%s)"), g_strerror (errno)); break; } if (outpipe >= 0 && FD_ISSET (outpipe, &fds)) { switch (read_data (outstr, outpipe, error)) { case READ_FAILED: failed = TRUE; break; case READ_EOF: close_and_invalidate (&outpipe); outpipe = -1; break; default: break; } if (failed) break; } if (errpipe >= 0 && FD_ISSET (errpipe, &fds)) { switch (read_data (errstr, errpipe, error)) { case READ_FAILED: failed = TRUE; break; case READ_EOF: close_and_invalidate (&errpipe); errpipe = -1; break; default: break; } if (failed) break; } } /* These should only be open still if we had an error. */ if (outpipe >= 0) close_and_invalidate (&outpipe); if (errpipe >= 0) close_and_invalidate (&errpipe); /* Wait for child to exit, even if we have * an error pending. */ again: ret = waitpid (pid, &status, 0); if (ret < 0) { if (errno == EINTR) goto again; else if (errno == ECHILD) { if (exit_status) { g_warning ("In call to g_spawn_sync(), exit status of a child process was requested but SIGCHLD action was set to SIG_IGN and ECHILD was received by waitpid(), so exit status can't be returned. This is a bug in the program calling g_spawn_sync(); either don't request the exit status, or don't set the SIGCHLD action."); } else { /* We don't need the exit status. */ } } else { if (!failed) /* avoid error pileups */ { failed = TRUE; g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, _("Unexpected error in waitpid() (%s)"), g_strerror (errno)); } } } if (failed) { if (outstr) g_string_free (outstr, TRUE); if (errstr) g_string_free (errstr, TRUE); return FALSE; } else { if (exit_status) *exit_status = status; if (standard_output) *standard_output = g_string_free (outstr, FALSE); if (standard_error) *standard_error = g_string_free (errstr, FALSE); return TRUE; } } /** * g_spawn_async_with_pipes: * @working_directory: child's current working directory, or NULL to inherit parent's * @argv: child's argument vector * @envp: child's environment, or NULL to inherit parent's * @flags: flags from #GSpawnFlags * @child_setup: function to run in the child just before exec() * @user_data: user data for @child_setup * @child_pid: return location for child process ID, or NULL * @standard_input: return location for file descriptor to write to child's stdin, or NULL * @standard_output: return location for file descriptor to read child's stdout, or NULL * @standard_error: return location for file descriptor to read child's stderr, or NULL * @error: return location for error * * Executes a child program asynchronously (your program will not * block waiting for the child to exit). The child program is * specified by the only argument that must be provided, @argv. @argv * should be a NULL-terminated array of strings, to be passed as the * argument vector for the child. The first string in @argv is of * course the name of the program to execute. By default, the name of * the program must be a full path; the PATH shell variable will only * be searched if you pass the %G_SPAWN_SEARCH_PATH flag. * * @envp is a NULL-terminated array of strings, where each string * has the form KEY=VALUE. This will become * the child's environment. If @envp is NULL, the child inherits its * parent's environment. * * @flags should be the bitwise OR of any flags you want to affect the * function's behavior. The %G_SPAWN_DO_NOT_REAP_CHILD means that the * child will not be automatically reaped; you must call waitpid() or * handle SIGCHLD yourself, or the child will become a zombie. * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file * descriptors will be inherited by the child; otherwise all * descriptors except stdin/stdout/stderr will be closed before * calling exec() in the child. %G_SPAWN_SEARCH_PATH means that * argv[0] need not be an absolute path, it * will be looked for in the user's PATH. %G_SPAWN_STDOUT_TO_DEV_NULL * means that the child's standad output will be discarded, instead * of going to the same location as the parent's standard output. * %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error * will be discarded. %G_SPAWN_CHILD_INHERITS_STDIN means that * the child will inherit the parent's standard input (by default, * the child's standard input is attached to /dev/null). * * @child_setup and @user_data are a function and user data to be * called in the child after GLib has performed all the setup it plans * to perform (including creating pipes, closing file descriptors, * etc.) but before calling exec(). That is, @child_setup is called * just before calling exec() in the child. Obviously actions taken in * this function will only affect the child, not the parent. * * If non-NULL, @child_pid will be filled with the child's process * ID. You can use the process ID to send signals to the child, or * to waitpid() if you specified the %G_SPAWN_DO_NOT_REAP_CHILD flag. * * If non-NULL, the @standard_input, @standard_output, @standard_error * locations will be filled with file descriptors for writing to the child's * standard input or reading from its standard output or standard error. * The caller of g_spawn_async_with_pipes() must close these file descriptors * when they are no longer in use. If these parameters are NULL, the * corresponding pipe won't be created. * * @error can be NULL to ignore errors, or non-NULL to report errors. * If an error is set, the function returns FALSE. Errors * are reported even if they occur in the child (for example if the * executable in argv[0] is not found). Typically * the message field of returned errors should be displayed * to users. Possible errors are those from the #G_SPAWN_ERROR domain. * * If an error occurs, @child_pid, @standard_input, @standard_output, * and @standard_error will not be filled with valid values. * * Return value: TRUE on success, FALSE if an error was set **/ gboolean g_spawn_async_with_pipes (const gchar *working_directory, gchar **argv, gchar **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, gpointer user_data, gint *child_pid, gint *standard_input, gint *standard_output, gint *standard_error, GError **error) { g_return_val_if_fail (argv != NULL, FALSE); g_return_val_if_fail (standard_output == NULL || !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE); g_return_val_if_fail (standard_error == NULL || !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE); /* can't inherit stdin if we have an input pipe. */ g_return_val_if_fail (standard_input == NULL || !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE); return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), working_directory, argv, envp, !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN), (flags & G_SPAWN_SEARCH_PATH) != 0, (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0, (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0, (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0, child_setup, user_data, child_pid, standard_input, standard_output, standard_error, error); } /** * g_spawn_command_line_sync: * @command_line: a command line * @standard_output: return location for child output * @standard_error: return location for child errors * @exit_status: return location for child exit status * @error: return location for errors * * A simple version of g_spawn_sync() with little-used parameters * removed, taking a command line instead of an argument vector. See * g_spawn_sync() for full details. @command_line will be parsed by * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security * implications, so consider using g_spawn_sync() directly if * appropriate. Possible errors are those from g_spawn_sync() and those * from g_shell_parse_argv(). * * Return value: TRUE on success, FALSE if an error was set **/ gboolean g_spawn_command_line_sync (const gchar *command_line, gchar **standard_output, gchar **standard_error, gint *exit_status, GError **error) { gboolean retval; gchar **argv = 0; g_return_val_if_fail (command_line != NULL, FALSE); if (!g_shell_parse_argv (command_line, NULL, &argv, error)) return FALSE; retval = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, standard_output, standard_error, exit_status, error); g_strfreev (argv); return retval; } /** * g_spawn_command_line_async: * @command_line: a command line * @error: return location for errors * * A simple version of g_spawn_async() that parses a command line with * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a * command line in the background. Unlike g_spawn_async(), the * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note * that %G_SPAWN_SEARCH_PATH can have security implications, so * consider using g_spawn_async() directly if appropriate. Possible * errors are those from g_shell_parse_argv() and g_spawn_async(). * * Return value: TRUE on success, FALSE if error is set. **/ gboolean g_spawn_command_line_async (const gchar *command_line, GError **error) { gboolean retval; gchar **argv = 0; g_return_val_if_fail (command_line != NULL, FALSE); if (!g_shell_parse_argv (command_line, NULL, &argv, error)) return FALSE; retval = g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, error); g_strfreev (argv); return retval; } static gint exec_err_to_g_error (gint en) { switch (en) { #ifdef EACCES case EACCES: return G_SPAWN_ERROR_ACCES; break; #endif #ifdef EPERM case EPERM: return G_SPAWN_ERROR_PERM; break; #endif #ifdef E2BIG case E2BIG: return G_SPAWN_ERROR_2BIG; break; #endif #ifdef ENOEXEC case ENOEXEC: return G_SPAWN_ERROR_NOEXEC; break; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return G_SPAWN_ERROR_NAMETOOLONG; break; #endif #ifdef ENOENT case ENOENT: return G_SPAWN_ERROR_NOENT; break; #endif #ifdef ENOMEM case ENOMEM: return G_SPAWN_ERROR_NOMEM; break; #endif #ifdef ENOTDIR case ENOTDIR: return G_SPAWN_ERROR_NOTDIR; break; #endif #ifdef ELOOP case ELOOP: return G_SPAWN_ERROR_LOOP; break; #endif #ifdef ETXTBUSY case ETXTBUSY: return G_SPAWN_ERROR_TXTBUSY; break; #endif #ifdef EIO case EIO: return G_SPAWN_ERROR_IO; break; #endif #ifdef ENFILE case ENFILE: return G_SPAWN_ERROR_NFILE; break; #endif #ifdef EMFILE case EMFILE: return G_SPAWN_ERROR_MFILE; break; #endif #ifdef EINVAL case EINVAL: return G_SPAWN_ERROR_INVAL; break; #endif #ifdef EISDIR case EISDIR: return G_SPAWN_ERROR_ISDIR; break; #endif #ifdef ELIBBAD case ELIBBAD: return G_SPAWN_ERROR_LIBBAD; break; #endif default: return G_SPAWN_ERROR_FAILED; break; } } static void write_err_and_exit (gint fd, gint msg) { gint en = errno; write (fd, &msg, sizeof(msg)); write (fd, &en, sizeof(en)); _exit (1); } static void set_cloexec (gint fd) { fcntl (fd, F_SETFD, FD_CLOEXEC); } static gint sane_dup2 (gint fd1, gint fd2) { gint ret; retry: ret = dup2 (fd1, fd2); if (ret < 0 && errno == EINTR) goto retry; return ret; } enum { CHILD_CHDIR_FAILED, CHILD_EXEC_FAILED, CHILD_DUP2_FAILED, CHILD_FORK_FAILED }; static void do_exec (gint child_err_report_fd, gint stdin_fd, gint stdout_fd, gint stderr_fd, const gchar *working_directory, gchar **argv, gchar **envp, gboolean close_descriptors, gboolean search_path, gboolean stdout_to_null, gboolean stderr_to_null, gboolean child_inherits_stdin, GSpawnChildSetupFunc child_setup, gpointer user_data) { if (working_directory && chdir (working_directory) < 0) write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED); /* Close all file descriptors but stdin stdout and stderr as * soon as we exec. Note that this includes * child_err_report_fd, which keeps the parent from blocking * forever on the other end of that pipe. */ if (close_descriptors) { gint open_max; gint i; open_max = sysconf (_SC_OPEN_MAX); for (i = 3; i < open_max; i++) set_cloexec (i); } else { /* We need to do child_err_report_fd anyway */ set_cloexec (child_err_report_fd); } /* Redirect pipes as required */ if (stdin_fd >= 0) { /* dup2 can't actually fail here I don't think */ if (sane_dup2 (stdin_fd, 0) < 0) write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); /* ignore this if it doesn't work */ close_and_invalidate (&stdin_fd); } else if (!child_inherits_stdin) { /* Keep process from blocking on a read of stdin */ gint read_null = open ("/dev/null", O_RDONLY); sane_dup2 (read_null, 0); close_and_invalidate (&read_null); } if (stdout_fd >= 0) { /* dup2 can't actually fail here I don't think */ if (sane_dup2 (stdout_fd, 1) < 0) write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); /* ignore this if it doesn't work */ close_and_invalidate (&stdout_fd); } else if (stdout_to_null) { gint write_null = open ("/dev/null", O_WRONLY); sane_dup2 (write_null, 1); close_and_invalidate (&write_null); } if (stderr_fd >= 0) { /* dup2 can't actually fail here I don't think */ if (sane_dup2 (stderr_fd, 2) < 0) write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED); /* ignore this if it doesn't work */ close_and_invalidate (&stderr_fd); } else if (stderr_to_null) { gint write_null = open ("/dev/null", O_WRONLY); sane_dup2 (write_null, 2); close_and_invalidate (&write_null); } /* Call user function just before we exec */ if (child_setup) { (* child_setup) (user_data); } g_execute (argv[0], argv, envp, search_path); /* Exec failed */ write_err_and_exit (child_err_report_fd, CHILD_EXEC_FAILED); } static gboolean read_ints (int fd, gint* buf, gint n_ints_in_buf, gint *n_ints_read, GError **error) { gint bytes = 0; while (TRUE) { gint chunk; if (bytes >= sizeof(gint)*2) break; /* give up, who knows what happened, should not be * possible. */ again: chunk = read (fd, ((gchar*)buf) + bytes, sizeof(gint)*n_ints_in_buf - bytes); if (chunk < 0 && errno == EINTR) goto again; if (chunk < 0) { /* Some weird shit happened, bail out */ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to read from child pipe (%s)"), g_strerror (errno)); return FALSE; } else if (chunk == 0) break; /* EOF */ else { g_assert (chunk > 0); bytes += chunk; } } *n_ints_read = bytes/4; return TRUE; } static gboolean fork_exec_with_pipes (gboolean intermediate_child, const gchar *working_directory, gchar **argv, gchar **envp, gboolean close_descriptors, gboolean search_path, gboolean stdout_to_null, gboolean stderr_to_null, gboolean child_inherits_stdin, GSpawnChildSetupFunc child_setup, gpointer user_data, gint *child_pid, gint *standard_input, gint *standard_output, gint *standard_error, GError **error) { gint pid; gint stdin_pipe[2] = { -1, -1 }; gint stdout_pipe[2] = { -1, -1 }; gint stderr_pipe[2] = { -1, -1 }; gint child_err_report_pipe[2] = { -1, -1 }; gint child_pid_report_pipe[2] = { -1, -1 }; gint status; if (!make_pipe (child_err_report_pipe, error)) return FALSE; if (intermediate_child && !make_pipe (child_pid_report_pipe, error)) goto cleanup_and_fail; if (standard_input && !make_pipe (stdin_pipe, error)) goto cleanup_and_fail; if (standard_output && !make_pipe (stdout_pipe, error)) goto cleanup_and_fail; if (standard_error && !make_pipe (stderr_pipe, error)) goto cleanup_and_fail; pid = fork (); if (pid < 0) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FORK, _("Failed to fork (%s)"), g_strerror (errno)); goto cleanup_and_fail; } else if (pid == 0) { /* Immediate child. This may or may not be the child that * actually execs the new process. */ /* Be sure we crash if the parent exits * and we write to the err_report_pipe */ signal (SIGPIPE, SIG_DFL); /* Close the parent's end of the pipes; * not needed in the close_descriptors case, * though */ close_and_invalidate (&child_err_report_pipe[0]); close_and_invalidate (&child_pid_report_pipe[0]); close_and_invalidate (&stdin_pipe[1]); close_and_invalidate (&stdout_pipe[0]); close_and_invalidate (&stderr_pipe[0]); if (intermediate_child) { /* We need to fork an intermediate child that launches the * final child. The purpose of the intermediate child * is to exit, so we can waitpid() it immediately. * Then the grandchild will not become a zombie. */ gint grandchild_pid; grandchild_pid = fork (); if (grandchild_pid < 0) { /* report -1 as child PID */ write (child_pid_report_pipe[1], &grandchild_pid, sizeof(grandchild_pid)); write_err_and_exit (child_err_report_pipe[1], CHILD_FORK_FAILED); } else if (grandchild_pid == 0) { do_exec (child_err_report_pipe[1], stdin_pipe[0], stdout_pipe[1], stderr_pipe[1], working_directory, argv, envp, close_descriptors, search_path, stdout_to_null, stderr_to_null, child_inherits_stdin, child_setup, user_data); } else { write (child_pid_report_pipe[1], &grandchild_pid, sizeof(grandchild_pid)); close_and_invalidate (&child_pid_report_pipe[1]); _exit (0); } } else { /* Just run the child. */ do_exec (child_err_report_pipe[1], stdin_pipe[0], stdout_pipe[1], stderr_pipe[1], working_directory, argv, envp, close_descriptors, search_path, stdout_to_null, stderr_to_null, child_inherits_stdin, child_setup, user_data); } } else { /* Parent */ gint buf[2]; gint n_ints = 0; /* Close the uncared-about ends of the pipes */ close_and_invalidate (&child_err_report_pipe[1]); close_and_invalidate (&child_pid_report_pipe[1]); close_and_invalidate (&stdin_pipe[0]); close_and_invalidate (&stdout_pipe[1]); close_and_invalidate (&stderr_pipe[1]); /* If we had an intermediate child, reap it */ if (intermediate_child) { wait_again: if (waitpid (pid, &status, 0) < 0) { if (errno == EINTR) goto wait_again; else if (errno == ECHILD) ; /* do nothing, child already reaped */ else g_warning ("waitpid() should not fail in " "'fork_exec_with_pipes'"); } } if (!read_ints (child_err_report_pipe[0], buf, 2, &n_ints, error)) goto cleanup_and_fail; if (n_ints >= 2) { /* Error from the child. */ switch (buf[0]) { case CHILD_CHDIR_FAILED: g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR, _("Failed to change to directory '%s' (%s)"), working_directory, g_strerror (buf[1])); break; case CHILD_EXEC_FAILED: g_set_error (error, G_SPAWN_ERROR, exec_err_to_g_error (buf[1]), _("Failed to execute child process (%s)"), g_strerror (buf[1])); break; case CHILD_DUP2_FAILED: g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to redirect output or input of child process (%s)"), g_strerror (buf[1])); break; case CHILD_FORK_FAILED: g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FORK, _("Failed to fork child process (%s)"), g_strerror (buf[1])); break; default: g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Unknown error executing child process")); break; } goto cleanup_and_fail; } /* Get child pid from intermediate child pipe. */ if (intermediate_child) { n_ints = 0; if (!read_ints (child_pid_report_pipe[0], buf, 1, &n_ints, error)) goto cleanup_and_fail; if (n_ints < 1) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to read enough data from child pid pipe (%s)"), g_strerror (errno)); goto cleanup_and_fail; } else { /* we have the child pid */ pid = buf[0]; } } /* Success against all odds! return the information */ if (child_pid) *child_pid = pid; if (standard_input) *standard_input = stdin_pipe[1]; if (standard_output) *standard_output = stdout_pipe[0]; if (standard_error) *standard_error = stderr_pipe[0]; return TRUE; } cleanup_and_fail: close_and_invalidate (&child_err_report_pipe[0]); close_and_invalidate (&child_err_report_pipe[1]); close_and_invalidate (&child_pid_report_pipe[0]); close_and_invalidate (&child_pid_report_pipe[1]); close_and_invalidate (&stdin_pipe[0]); close_and_invalidate (&stdin_pipe[1]); close_and_invalidate (&stdout_pipe[0]); close_and_invalidate (&stdout_pipe[1]); close_and_invalidate (&stderr_pipe[0]); close_and_invalidate (&stderr_pipe[1]); return FALSE; } static gboolean make_pipe (gint p[2], GError **error) { if (pipe (p) < 0) { g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to create pipe for communicating with child process (%s)"), g_strerror (errno)); return FALSE; } else return TRUE; } /* Based on execvp from GNU C Library */ static void script_execute (const gchar *file, gchar **argv, gchar **envp, gboolean search_path) { /* Count the arguments. */ int argc = 0; while (argv[argc]) ++argc; /* Construct an argument list for the shell. */ { gchar **new_argv; new_argv = g_new0 (gchar*, argc + 1); new_argv[0] = (char *) "/bin/sh"; new_argv[1] = (char *) file; while (argc > 1) { new_argv[argc] = argv[argc - 1]; --argc; } /* Execute the shell. */ if (envp) execve (new_argv[0], new_argv, envp); else execv (new_argv[0], new_argv); g_free (new_argv); } } static gchar* my_strchrnul (const gchar *str, gchar c) { gchar *p = (gchar*) str; while (*p && (*p != c)) ++p; return p; } static gint g_execute (const gchar *file, gchar **argv, gchar **envp, gboolean search_path) { if (*file == '\0') { /* We check the simple case first. */ errno = ENOENT; return -1; } if (!search_path || strchr (file, '/') != NULL) { /* Don't search when it contains a slash. */ if (envp) execve (file, argv, envp); else execv (file, argv); if (errno == ENOEXEC) script_execute (file, argv, envp, FALSE); } else { gboolean got_eacces = 0; const gchar *path, *p; gchar *name, *freeme; size_t len; size_t pathlen; path = g_getenv ("PATH"); if (path == NULL) { /* There is no `PATH' in the environment. The default * search path in libc is the current directory followed by * the path `confstr' returns for `_CS_PATH'. */ /* In GLib we put . last, for security, and don't use the * unportable confstr(); UNIX98 does not actually specify * what to search if PATH is unset. POSIX may, dunno. */ path = "/bin:/usr/bin:."; } len = strlen (file) + 1; pathlen = strlen (path); freeme = name = g_malloc (pathlen + len + 1); /* Copy the file name at the top, including '\0' */ memcpy (name + pathlen + 1, file, len); name = name + pathlen; /* And add the slash before the filename */ *name = '/'; p = path; do { char *startp; path = p; p = my_strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end * of `PATH' means to search the current directory. */ startp = name + 1; else startp = memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execv will not return. */ if (envp) execve (startp, argv, envp); else execv (startp, argv); if (errno == ENOEXEC) script_execute (startp, argv, envp, search_path); switch (errno) { case EACCES: /* Record the we got a `Permission denied' error. If we end * up finding no executable we can use, we want to diagnose * that we did find one but were denied access. */ got_eacces = TRUE; /* FALL THRU */ case ENOENT: #ifdef ESTALE case ESTALE: #endif #ifdef ENOTDIR case ENOTDIR: #endif /* Those errors indicate the file is missing or not executable * by us, in which case we want to just try the next path * directory. */ break; default: /* Some other error means we found an executable file, but * something went wrong executing it; return the error to our * caller. */ g_free (freeme); return -1; } } while (*p++ != '\0'); /* We tried every element and none of them worked. */ if (got_eacces) /* At least one failure was due to permissions, so report that * error. */ errno = EACCES; g_free (freeme); } /* Return the error from the last attempt (probably ENOENT). */ return -1; }