summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnders Carlsson <andersca@codefactory.se>2003-02-15 17:18:13 +0000
committerAnders Carlsson <andersca@codefactory.se>2003-02-15 17:18:13 +0000
commit2aa38be20b015777ec6570d1e2fd7e072f5f3be9 (patch)
treee08632fff63b7edc17c47af46fb6c54a55e8960a
parent0c502d5bc3c1858acb78d57439767f8ef24f90fe (diff)
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.
-rw-r--r--ChangeLog15
-rw-r--r--dbus/dbus-errors.c18
-rw-r--r--dbus/dbus-errors.h6
-rw-r--r--dbus/dbus-sysdeps.c272
-rw-r--r--dbus/dbus-sysdeps.h5
-rw-r--r--test/Makefile.am6
-rw-r--r--test/spawn-test.c34
7 files changed, 347 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 24abc4e2..6c5517b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
+}