diff options
| -rw-r--r-- | ChangeLog | 15 | ||||
| -rw-r--r-- | dbus/dbus-errors.c | 18 | ||||
| -rw-r--r-- | dbus/dbus-errors.h | 6 | ||||
| -rw-r--r-- | dbus/dbus-sysdeps.c | 272 | ||||
| -rw-r--r-- | dbus/dbus-sysdeps.h | 5 | ||||
| -rw-r--r-- | test/Makefile.am | 6 | ||||
| -rw-r--r-- | test/spawn-test.c | 34 | 
7 files changed, 347 insertions, 9 deletions
@@ -1,5 +1,20 @@  2003-02-15  Anders Carlsson  <andersca@codefactory.se> +	* dbus/dbus-errors.c: (dbus_set_error): +	* dbus/dbus-errors.h: +	Add a few errors and make dbus_set_error void. +	 +	* dbus/dbus-sysdeps.c:  +	(_dbus_errno_to_string), (close_and_invalidate), (make_pipe), +	(write_err_and_exit), (read_ints), (do_exec), (_dbus_spawn_async): +	* dbus/dbus-sysdeps.h: +	Add _dbus_spawn_async. +	 +	* test/spawn-test.c: (main): +	Test for _dbus_spawn_async. +	 +2003-02-15  Anders Carlsson  <andersca@codefactory.se> +  	* dbus/dbus-internals.h:  	Fix build without tests. diff --git a/dbus/dbus-errors.c b/dbus/dbus-errors.c index a679e6c6..dfc52fb3 100644 --- a/dbus/dbus-errors.c +++ b/dbus/dbus-errors.c @@ -214,12 +214,14 @@ dbus_set_error_const (DBusError  *error,   * Assigns an error name and message to a DBusError.   * Does nothing if error is #NULL.   * + * If no memory can be allocated for the error message,  + * an out-of-memory error message will be set instead. + *    * @param error the error.   * @param name the error name (not copied!!!)   * @param format printf-style format string. - * @returns #TRUE on success.   */ -dbus_bool_t +void  dbus_set_error (DBusError  *error,  		const char *name,  		const char *format, @@ -232,7 +234,7 @@ dbus_set_error (DBusError  *error,    char c;    if (error == NULL) -    return TRUE; +    return;    va_start (args, format); @@ -246,8 +248,12 @@ dbus_set_error (DBusError  *error,    vsprintf (message, format, args2);    if (!message) -    return FALSE; - +    { +      dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, +			    "Failed to allocate memory for error message."); +      return; +    } +      va_end (args);    dbus_error_init (error); @@ -256,8 +262,6 @@ dbus_set_error (DBusError  *error,    real->name = name;    real->message = message;    real->const_message = FALSE; -   -  return TRUE;  }  /** @} */ diff --git a/dbus/dbus-errors.h b/dbus/dbus-errors.h index 639c6a70..5cc77491 100644 --- a/dbus/dbus-errors.h +++ b/dbus/dbus-errors.h @@ -49,6 +49,10 @@ struct DBusError    void *padding1; /**< placeholder */  }; +#define DBUS_ERROR_SPAWN_FORK_FAILED "org.freedesktop.DBus.Error.Spawn.ForkFailed" +#define DBUS_ERROR_SPAWN_FAILED      "org.freedesktop.DBus.Error.Spawn.Failed" +#define DBUS_ERROR_NO_MEMORY         "org.freedesktop.DBus.Error.NoMemory" +  typedef enum  {    DBUS_RESULT_SUCCESS,         /**< Operation was successful. */ @@ -75,7 +79,7 @@ typedef enum  void        dbus_error_init      (DBusError  *error);  void        dbus_error_free      (DBusError  *error); -dbus_bool_t dbus_set_error       (DBusError  *error, +void        dbus_set_error       (DBusError  *error,  				  const char *name,  				  const char *message,  				  ...); diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index 53eebf77..e1ae16c3 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -39,6 +39,7 @@  #include <time.h>  #include <sys/time.h>  #include <sys/stat.h> +#include <sys/wait.h>  #ifdef HAVE_WRITEV  #include <sys/uio.h> @@ -1450,4 +1451,275 @@ _dbus_generate_random_bytes (DBusString *str,    return FALSE;  } +const char * +_dbus_errno_to_string (int errnum) +{ +  return strerror (errnum); +} + +/* 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 int +close_and_invalidate (int *fd) +{ +  int ret; + +  if (*fd < 0) +    return -1; +  else +    { +      ret = close (*fd); +      *fd = -1; +    } + +  return ret; +} + +static dbus_bool_t +make_pipe (int        p[2], +           DBusError *error) +{ +  if (pipe (p) < 0) +    { +      dbus_set_error (error, +		      DBUS_ERROR_SPAWN_FAILED, +		      "Failed to create pipe for communicating with child process (%s)", +		      _dbus_errno_to_string (errno)); +      return FALSE; +    } +  else +    return TRUE; +} + +enum +{ +  CHILD_CHDIR_FAILED, +  CHILD_EXEC_FAILED, +  CHILD_DUP2_FAILED, +  CHILD_FORK_FAILED +}; + +static void +write_err_and_exit (int fd, int msg) +{ +  int en = errno; +   +  write (fd, &msg, sizeof(msg)); +  write (fd, &en, sizeof(en)); +   +  _exit (1); +} + +static dbus_bool_t +read_ints (int        fd, +	   int       *buf, +	   int        n_ints_in_buf, +	   int       *n_ints_read, +	   DBusError *error) +{ +  size_t bytes = 0;     +   +  while (TRUE) +    { +      size_t chunk;     + +      if (bytes >= sizeof(int)*2) +        break; /* give up, who knows what happened, should not be +                * possible. +                */ +           +    again: +      chunk = read (fd, +                    ((char*)buf) + bytes, +                    sizeof(int) * n_ints_in_buf - bytes); +      if (chunk < 0 && errno == EINTR) +        goto again; +           +      if (chunk < 0) +        { +          /* Some weird shit happened, bail out */ +               +          dbus_set_error (error, +			  DBUS_ERROR_SPAWN_FAILED, +			  "Failed to read from child pipe (%s)", +			  _dbus_errno_to_string (errno)); + +          return FALSE; +        } +      else if (chunk == 0) +        break; /* EOF */ +      else /* chunk > 0 */ +	bytes += chunk; +    } + +  *n_ints_read = (int)(bytes / sizeof(int)); + +  return TRUE; +} + +static void +do_exec (int    child_err_report_fd, +	 char **argv) +{ +  execvp (argv[0], argv); + +  /* Exec failed */ +  write_err_and_exit (child_err_report_fd, +                      CHILD_EXEC_FAILED); +   +} + +dbus_bool_t +_dbus_spawn_async (char      **argv, +		   DBusError  *error) +{ +  int pid = -1, grandchild_pid; +  int child_err_report_pipe[2] = { -1, -1 }; +  int child_pid_report_pipe[2] = { -1, -1 }; +  int status; +   +  printf ("spawning application: %s\n", argv[0]); + +  if (!make_pipe (child_err_report_pipe, error)) +    return FALSE; + +  if (!make_pipe (child_pid_report_pipe, error)) +    goto cleanup_and_fail; +   +  pid = fork (); + +  if (pid < 0) +    { +      dbus_set_error (error, +		      DBUS_ERROR_SPAWN_FORK_FAILED, +		      "Failed to fork (%s)", +		      _dbus_errno_to_string (errno)); +      return FALSE; +    } +  else if (pid == 0) +    { +      /* Immediate child. */ +       +      /* 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]); + +      /* 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. +       */ +      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], +		   argv); +	} +      else +	{ +	  write (child_pid_report_pipe[1], &grandchild_pid, sizeof(grandchild_pid)); +	  close_and_invalidate (&child_pid_report_pipe[1]); +               +	  _exit (0); +	} +    } +  else +    { +      /* Parent */ + +      int buf[2]; +      int 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]); + +    wait_again: +      if (waitpid (pid, &status, 0) < 0) +	{ +	  if (errno == EINTR) +	    goto wait_again; +	  else if (errno == ECHILD) +	    ; /* do nothing, child already reaped */ +	  else +	    _dbus_warn ("waitpid() should not fail in " +			"'_dbus_spawn_async'"); +	} + +      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]) +            { +	    default: +              dbus_set_error (error, +			      DBUS_ERROR_SPAWN_FAILED, +			      "Unknown error executing child process \"%s\"", +			      argv[0]); +              break; +	    } + +	  goto cleanup_and_fail; +	} + + +      /* Success against all odds! return the information */ +      close_and_invalidate (&child_err_report_pipe[0]); + +      return TRUE; +    } + + cleanup_and_fail: + +  /* There was an error from the Child, reap the child to avoid it being +     a zombie. +  */ +  if (pid > 0) +    { +    wait_failed: +      if (waitpid (pid, NULL, 0) < 0) +	{ +          if (errno == EINTR) +            goto wait_failed; +          else if (errno == ECHILD) +            ; /* do nothing, child already reaped */ +          else +            _dbus_warn ("waitpid() should not fail in " +			"'_dbus_spawn_async'"); +	} +    } +   +  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]); + +  return FALSE; +} +  /** @} end of sysdeps */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 76c943bd..dca12ed3 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -147,6 +147,11 @@ void         _dbus_directory_close         (DBusDirIter      *iter);  dbus_bool_t _dbus_generate_random_bytes (DBusString *str,                                           int         n_bytes); +const char *_dbus_errno_to_string (int         errnum); +dbus_bool_t _dbus_spawn_async     (char      **argv, +				   DBusError  *error); + +			         DBUS_END_DECLS;  #endif /* DBUS_SYSDEPS_H */ diff --git a/test/Makefile.am b/test/Makefile.am index ad657826..604fd3f8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,7 @@  INCLUDES=-I$(top_srcdir) $(DBUS_TEST_CFLAGS)   if DBUS_BUILD_TESTS -TEST_BINARIES=echo-client echo-server unbase64 bus-test break-loader +TEST_BINARIES=echo-client echo-server unbase64 bus-test break-loader spawn-test  else  TEST_BINARIES=  endif @@ -31,6 +31,9 @@ bus_test_SOURCES = 				\  break_loader_SOURCES=				\  	break-loader.c +spawn_test_SOURCES=				\ +	spawn-test.c +  TEST_LIBS=$(DBUS_TEST_LIBS) $(top_builddir)/dbus/libdbus-convenience.la $(top_builddir)/dbus/libdbus-1.la  echo_client_LDADD=$(TEST_LIBS) @@ -38,6 +41,7 @@ echo_server_LDADD=$(TEST_LIBS)  unbase64_LDADD=$(TEST_LIBS)  break_loader_LDADD= $(TEST_LIBS)  bus_test_LDADD=$(TEST_LIBS) $(top_builddir)/bus/libdbus-daemon.la +spawn_test_LDADD=$(TEST_LIBS)  dist-hook:											   \  	DIRS="data data/valid-messages data/invalid-messages data/incomplete-messages data/auth" ; \ diff --git a/test/spawn-test.c b/test/spawn-test.c new file mode 100644 index 00000000..fda0309b --- /dev/null +++ b/test/spawn-test.c @@ -0,0 +1,34 @@ +#include <dbus/dbus.h> + +#define DBUS_COMPILATION /* cheat and use dbus-sysdeps */ +#include <dbus/dbus-sysdeps.h> +#undef DBUS_COMPILATION +#include <stdio.h> + +int +main (int argc, char **argv) +{ +  char **argv_copy; +  int i; +  DBusError error; +   +  if (argc < 2) +    { +      fprintf (stderr, "You need to specify a program to launch.\n"); + +      return -1; +    } + +  argv_copy = dbus_new (char *, argc); +  for (i = 0; i < argc - 1; i++) +    argv_copy [i] = argv[i + 1]; +  argv_copy[argc - 1] = NULL; +   +  if (!_dbus_spawn_async (argv_copy, &error)) +    { +      fprintf (stderr, "Could not launch application: \"%s\"\n", +	       error.message); +    } +   +  return 0; +}  | 
