mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-06-19 09:38:27 +02:00
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.
This commit is contained in:
parent
0c502d5bc3
commit
2aa38be20b
7 changed files with 347 additions and 9 deletions
15
ChangeLog
15
ChangeLog
|
|
@ -1,3 +1,18 @@
|
|||
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" ; \
|
||||
|
|
|
|||
34
test/spawn-test.c
Normal file
34
test/spawn-test.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue