123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- /*
- * Spawning processes.
- *
- * Author:
- * Gonzalo Paniagua Javier (gonzalo@novell.com
- *
- * (C) 2006 Novell, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <glib.h>
- #ifdef HAVE_UNISTD_H
- #ifndef __USE_GNU
- #define __USE_GNU
- #endif
- #include <unistd.h>
- #endif
- #ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #ifdef HAVE_SYS_WAIT_H
- #include <sys/wait.h>
- #endif
- #ifdef HAVE_SYS_RESOURCE_H
- # include <sys/resource.h>
- #endif
- #ifdef G_OS_WIN32
- #include <io.h>
- #include <winsock2.h>
- #define open _open
- #define close _close
- #define read _read
- #define write _write
- /* windows pipe api details: http://msdn2.microsoft.com/en-us/library/edze9h7e(VS.80).aspx */
- #define pipe(x) _pipe(x, 256, 0)
- #endif
- #if HAVE_G_SPAWN
- #define set_error(msg, ...) do { if (gerror != NULL) *gerror = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
- #define set_error_cond(cond,msg, ...) do { if ((cond) && gerror != NULL) *gerror = g_error_new (G_LOG_DOMAIN, 1, msg, __VA_ARGS__); } while (0)
- #define set_error_status(status,msg, ...) do { if (gerror != NULL) *gerror = g_error_new (G_LOG_DOMAIN, status, msg, __VA_ARGS__); } while (0)
- #define NO_INTR(var,cmd) do { (var) = (cmd); } while ((var) == -1 && errno == EINTR)
- static void
- mono_close_pipe (int p [2])
- {
- close (p [0]);
- close (p [1]);
- }
- #if defined(__APPLE__)
- #if defined (TARGET_OSX)
- /* Apple defines this in crt_externs.h but doesn't provide that header for
- * arm-apple-darwin9. We'll manually define the symbol on Apple as it does
- * in fact exist on all implementations (so far)
- */
- G_BEGIN_DECLS
- gchar ***_NSGetEnviron(void);
- G_END_DECLS
- #define environ (*_NSGetEnviron())
- #else
- static char *mono_environ[1] = { NULL };
- #define environ mono_environ
- #endif /* defined (TARGET_OSX) */
- #elif defined(_MSC_VER)
- /* MS defines this in stdlib.h */
- #else
- G_BEGIN_DECLS
- extern char **environ;
- G_END_DECLS
- #endif
- static int
- safe_read (int fd, gchar *buffer, gint count, GError **gerror)
- {
- int res;
- NO_INTR (res, read (fd, buffer, count));
- set_error_cond (res == -1, "%s", "Error reading from pipe.");
- return res;
- }
- static int
- read_pipes (int outfd, gchar **out_str, int errfd, gchar **err_str, GError **gerror)
- {
- fd_set rfds;
- int res;
- gboolean out_closed;
- gboolean err_closed;
- GString *out = NULL;
- GString *err = NULL;
- gchar *buffer = NULL;
- gint nread;
- out_closed = (outfd < 0);
- err_closed = (errfd < 0);
- if (out_str) {
- *out_str = NULL;
- out = g_string_new ("");
- }
- if (err_str) {
- *err_str = NULL;
- err = g_string_new ("");
- }
- do {
- if (out_closed && err_closed)
- break;
- FD_ZERO (&rfds);
- if (!out_closed && outfd >= 0)
- FD_SET (outfd, &rfds);
- if (!err_closed && errfd >= 0)
- FD_SET (errfd, &rfds);
- res = select (MAX (outfd, errfd) + 1, &rfds, NULL, NULL, NULL);
- if (res > 0) {
- if (buffer == NULL)
- buffer = g_malloc (1024);
- if (!out_closed && FD_ISSET (outfd, &rfds)) {
- nread = safe_read (outfd, buffer, 1024, gerror);
- if (nread < 0) {
- close (errfd);
- close (outfd);
- return -1;
- }
- g_string_append_len (out, buffer, nread);
- if (nread <= 0) {
- out_closed = TRUE;
- close (outfd);
- }
- }
- if (!err_closed && FD_ISSET (errfd, &rfds)) {
- nread = safe_read (errfd, buffer, 1024, gerror);
- if (nread < 0) {
- close (errfd);
- close (outfd);
- return -1;
- }
- g_string_append_len (err, buffer, nread);
- if (nread <= 0) {
- err_closed = TRUE;
- close (errfd);
- }
- }
- }
- } while (res > 0 || (res == -1 && errno == EINTR));
- g_free (buffer);
- if (out_str)
- *out_str = g_string_free (out, FALSE);
- if (err_str)
- *err_str = g_string_free (err, FALSE);
- return 0;
- }
- static gboolean
- create_pipe (int *fds, GError **gerror)
- {
- if (pipe (fds) == -1) {
- set_error ("%s", "Error creating pipe.");
- return FALSE;
- }
- return TRUE;
- }
- static int
- write_all (int fd, const void *vbuf, size_t n)
- {
- const char *buf = (const char *) vbuf;
- size_t nwritten = 0;
- int w;
-
- do {
- do {
- w = write (fd, buf + nwritten, n - nwritten);
- } while (w == -1 && errno == EINTR);
-
- if (w == -1)
- return -1;
-
- nwritten += w;
- } while (nwritten < n);
-
- return nwritten;
- }
- #endif // HAVE_G_SPAWN
- #if !defined(G_OS_WIN32) && defined(HAVE_GETDTABLESIZE)
- int
- eg_getdtablesize (void)
- {
- #ifdef HAVE_GETRLIMIT
- struct rlimit limit;
- int res;
- res = getrlimit (RLIMIT_NOFILE, &limit);
- g_assert (res == 0);
- return limit.rlim_cur;
- #else
- return getdtablesize ();
- #endif
- }
- #else
- int
- eg_getdtablesize (void)
- {
- g_error ("Should not be called");
- }
- #endif
- #if HAVE_G_SPAWN
- gboolean
- g_spawn_command_line_sync (const gchar *command_line,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **gerror)
- {
- pid_t pid;
- gchar **argv;
- gint argc;
- int stdout_pipe [2] = { -1, -1 };
- int stderr_pipe [2] = { -1, -1 };
- int status;
- int res;
-
- if (!g_shell_parse_argv (command_line, &argc, &argv, gerror))
- return FALSE;
- if (standard_output && !create_pipe (stdout_pipe, gerror))
- return FALSE;
- if (standard_error && !create_pipe (stderr_pipe, gerror)) {
- if (standard_output) {
- mono_close_pipe (stdout_pipe);
- }
- return FALSE;
- }
- pid = fork ();
- if (pid == 0) {
- gint i;
- if (standard_output) {
- close (stdout_pipe [0]);
- dup2 (stdout_pipe [1], STDOUT_FILENO);
- }
- if (standard_error) {
- close (stderr_pipe [0]);
- dup2 (stderr_pipe [1], STDERR_FILENO);
- }
- for (i = eg_getdtablesize () - 1; i >= 3; i--)
- close (i);
- /* G_SPAWN_SEARCH_PATH is always enabled for g_spawn_command_line_sync */
- if (!g_path_is_absolute (argv [0])) {
- gchar *arg0;
- arg0 = g_find_program_in_path (argv [0]);
- if (arg0 == NULL) {
- exit (1);
- }
- //g_free (argv [0]);
- argv [0] = arg0;
- }
- execv (argv [0], argv);
- exit (1); /* TODO: What now? */
- }
- g_strfreev (argv);
- if (standard_output)
- close (stdout_pipe [1]);
- if (standard_error)
- close (stderr_pipe [1]);
- if (standard_output || standard_error) {
- res = read_pipes (stdout_pipe [0], standard_output, stderr_pipe [0], standard_error, gerror);
- if (res) {
- waitpid (pid, &status, WNOHANG); /* avoid zombie */
- return FALSE;
- }
- }
- NO_INTR (res, waitpid (pid, &status, 0));
- /* TODO: What if error? */
- if (WIFEXITED (status) && exit_status) {
- *exit_status = WEXITSTATUS (status);
- }
- return TRUE;
- }
- /*
- * This is the only use we have in mono/metadata
- !g_spawn_async_with_pipes (NULL, (char**)addr_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &child_pid, &ch_in, &ch_out, NULL, NULL)
- */
- 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 **gerror)
- {
- pid_t pid;
- int info_pipe [2];
- int in_pipe [2] = { -1, -1 };
- int out_pipe [2] = { -1, -1 };
- int err_pipe [2] = { -1, -1 };
- int status;
- g_return_val_if_fail (argv != NULL, FALSE); /* Only mandatory arg */
- if (!create_pipe (info_pipe, gerror))
- return FALSE;
- if (standard_output && !create_pipe (out_pipe, gerror)) {
- mono_close_pipe (info_pipe);
- return FALSE;
- }
- if (standard_error && !create_pipe (err_pipe, gerror)) {
- mono_close_pipe (info_pipe);
- mono_close_pipe (out_pipe);
- return FALSE;
- }
- if (standard_input && !create_pipe (in_pipe, gerror)) {
- mono_close_pipe (info_pipe);
- mono_close_pipe (out_pipe);
- mono_close_pipe (err_pipe);
- return FALSE;
- }
- pid = fork ();
- if (pid == -1) {
- mono_close_pipe (info_pipe);
- mono_close_pipe (out_pipe);
- mono_close_pipe (err_pipe);
- mono_close_pipe (in_pipe);
- set_error ("%s", "Error in fork ()");
- return FALSE;
- }
- if (pid == 0) {
- /* No zombie left behind */
- if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
- pid = fork ();
- }
- if (pid != 0) {
- exit (pid == -1 ? 1 : 0);
- } else {
- gint i;
- int fd;
- gchar *arg0;
- gchar **actual_args;
- gint unused;
- close (info_pipe [0]);
- close (in_pipe [1]);
- close (out_pipe [0]);
- close (err_pipe [0]);
- /* when exec* succeeds, we want to close this fd, which will return
- * a 0 read on the parent. We're not supposed to keep it open forever.
- * If exec fails, we still can write the error to it before closing.
- */
- fcntl (info_pipe [1], F_SETFD, FD_CLOEXEC);
- if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
- pid = getpid ();
- NO_INTR (unused, write_all (info_pipe [1], &pid, sizeof (pid_t)));
- }
- if (working_directory && chdir (working_directory) == -1) {
- int err = errno;
- NO_INTR (unused, write_all (info_pipe [1], &err, sizeof (int)));
- exit (0);
- }
- if (standard_output) {
- dup2 (out_pipe [1], STDOUT_FILENO);
- } else if ((flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0) {
- fd = open ("/dev/null", O_WRONLY);
- dup2 (fd, STDOUT_FILENO);
- }
- if (standard_error) {
- dup2 (err_pipe [1], STDERR_FILENO);
- } else if ((flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0) {
- fd = open ("/dev/null", O_WRONLY);
- dup2 (fd, STDERR_FILENO);
- }
- if (standard_input) {
- dup2 (in_pipe [0], STDIN_FILENO);
- } else if ((flags & G_SPAWN_CHILD_INHERITS_STDIN) == 0) {
- fd = open ("/dev/null", O_RDONLY);
- dup2 (fd, STDIN_FILENO);
- }
- if ((flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN) != 0) {
- for (i = eg_getdtablesize () - 1; i >= 3; i--)
- close (i);
- }
- actual_args = ((flags & G_SPAWN_FILE_AND_ARGV_ZERO) == 0) ? argv : argv + 1;
- if (envp == NULL)
- envp = environ;
- if (child_setup)
- child_setup (user_data);
- arg0 = argv [0];
- if (!g_path_is_absolute (arg0) || (flags & G_SPAWN_SEARCH_PATH) != 0) {
- arg0 = g_find_program_in_path (argv [0]);
- if (arg0 == NULL) {
- int err = ENOENT;
- write_all (info_pipe [1], &err, sizeof (int));
- exit (0);
- }
- }
- execve (arg0, actual_args, envp);
- int const err = errno;
- write_all (info_pipe [1], &err, sizeof (int));
- exit (0);
- }
- } else if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
- int w;
- /* Wait for the first child if two are created */
- NO_INTR (w, waitpid (pid, &status, 0));
- if (status == 1 || w == -1) {
- mono_close_pipe (info_pipe);
- mono_close_pipe (out_pipe);
- mono_close_pipe (err_pipe);
- mono_close_pipe (in_pipe);
- set_error ("Error in fork (): %d", status);
- return FALSE;
- }
- }
- close (info_pipe [1]);
- close (in_pipe [0]);
- close (out_pipe [1]);
- close (err_pipe [1]);
- if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) == 0) {
- int x;
- NO_INTR (x, read (info_pipe [0], &pid, sizeof (pid_t))); /* if we read < sizeof (pid_t)... */
- }
- if (child_pid) {
- *child_pid = pid;
- }
- if (read (info_pipe [0], &status, sizeof (int)) != 0) {
- close (info_pipe [0]);
- close (in_pipe [0]);
- close (out_pipe [1]);
- close (err_pipe [1]);
- set_error_status (status, "Error in exec (%d -> %s)", status, strerror (status));
- return FALSE;
- }
- close (info_pipe [0]);
- if (standard_input)
- *standard_input = in_pipe [1];
- if (standard_output)
- *standard_output = out_pipe [0];
- if (standard_error)
- *standard_error = err_pipe [0];
- return TRUE;
- }
- #endif // HAVE_G_SPAWN
- #define MONO_EMPTY_SOURCE_FILE(x) extern const char mono_quash_linker_empty_file_warning_ ## x; \
- const char mono_quash_linker_empty_file_warning_ ## x = 0;
- MONO_EMPTY_SOURCE_FILE (gspawn);
|