Commit 2aa38be2 authored by Anders Carlsson's avatar Anders Carlsson

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.
parent 0c502d5b
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:
......
......@@ -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;
}
/** @} */
......@@ -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,
...);
......
......@@ -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 */
......@@ -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 */
......@@ -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" ; \
......
#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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment