PolicyKit: Branch 'master'
David Zeuthen
david at kemper.freedesktop.org
Sun Nov 11 13:35:54 PST 2007
src/kit/kit-spawn.c | 607 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/kit/kit-spawn.h | 49 ++++
2 files changed, 656 insertions(+)
New commits:
commit b3f4fe27d5c8494b9aed58883a1740102526628c
Author: David Zeuthen <davidz at redhat.com>
Date: Sun Nov 11 16:32:22 2007 -0500
actually include the files with the spawn functions
diff --git a/src/kit/kit-spawn.c b/src/kit/kit-spawn.c
new file mode 100644
index 0000000..fb48a14
--- /dev/null
+++ b/src/kit/kit-spawn.c
@@ -0,0 +1,607 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/***************************************************************************
+ *
+ * kit-spawn.c : Spawn utilities
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at fubar.dk>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <kit/kit.h>
+#include "kit-test.h"
+
+
+/**
+ * SECTION:kit-spawn
+ * @title: Spawn utilities
+ * @short_description: Spawn utilities
+ *
+ * Various spawn utilities.
+ **/
+
+static kit_bool_t
+set_close_on_exec (int fd, void *data)
+{
+ if (fd >= (int) data) {
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0 && errno != EBADF) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static kit_bool_t
+_fdwalk (kit_bool_t (*callback)(int fd, void *user_data), void *user_data)
+{
+ int fd;
+ int max_fd;
+
+ kit_return_val_if_fail (callback != NULL, FALSE);
+
+ max_fd = sysconf (_SC_OPEN_MAX);
+ for (fd = 0; fd < max_fd; fd++) {
+ if (!callback (fd, user_data))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+_sane_dup2 (int fd1, int fd2)
+{
+ int ret;
+
+again:
+ ret = dup2 (fd1, fd2);
+ if (ret < 0 && errno == EINTR)
+ goto again;
+
+ return ret;
+}
+
+static ssize_t
+_read_from (int fd, char **out_string)
+{
+ char buf[4096];
+ ssize_t num_read;
+
+again:
+ num_read = read (fd, buf, sizeof (buf) - 1);
+ if (num_read == -1) {
+ if (errno == EINTR)
+ goto again;
+ else
+ goto out;
+ }
+
+ if (num_read > 0) {
+ char *s;
+
+ buf[num_read] = '\0';
+
+ s = kit_str_append (*out_string, buf);
+ if (s == NULL) {
+ errno = ENOMEM;
+ num_read = -1;
+ goto out;
+ }
+ *out_string = s;
+
+ //kit_debug ("fd=%d read %d bytes: '%s'", fd, num_read, buf);
+ }
+
+out:
+ return num_read;
+}
+
+static ssize_t
+_write_to (int fd, char *str)
+{
+ ssize_t num_written;
+
+again:
+ num_written = write (fd, str, strlen (str));
+ if (num_written == -1) {
+ if (errno == EINTR)
+ goto again;
+ else
+ goto out;
+ }
+
+ kit_debug ("Wrote %d bytes from '%s'", num_written, str);
+
+out:
+ return num_written;
+}
+
+/**
+ * kit_spawn_sync:
+ * @working_directory: Working directory for child or #NULL to inherit parents
+ * @argv: #NULL terminated argument vector
+ * @envp: #NULL terminated environment or #NULL to inherit parents;
+ * @stdin: String to write to stdin of child or #NULL
+ * @stdout: Return location for stdout from child or #NULL. Free with kit_free().
+ * @stderr: Return location for stderr from child or #NULL. Free with kit_free().
+ * @out_exit_status: Return location for exit status
+ *
+ * Executes a child process and waits for the child process to exit
+ * before returning. The exit status of the child is stored in
+ * @out_exit_status as it would be returned by waitpid(); standard
+ * UNIX macros such as WIFEXITED() and WEXITSTATUS() must be used to
+ * evaluate the exit status.
+ *
+ * Returns: #TRUE if the child was executed; #FALSE if an error
+ * occured and errno will be set.
+ */
+kit_bool_t
+kit_spawn_sync (const char *working_directory,
+ char **argv,
+ char **envp,
+ char *stdin,
+ char **stdout,
+ char **stderr,
+ int *out_exit_status)
+{
+ kit_bool_t ret;
+ pid_t pid;
+ char **envp_to_use;
+ int stdin_pipe[2] = {-1, -1};
+ int stdout_pipe[2] = {-1, -1};
+ int stderr_pipe[2] = {-1, -1};
+
+ ret = FALSE;
+ pid = -1;
+
+ kit_return_val_if_fail (argv != NULL, FALSE);
+ kit_return_val_if_fail (out_exit_status != NULL, FALSE);
+
+ if (stdout != NULL)
+ *stdout = NULL;
+ if (stderr != NULL)
+ *stderr = NULL;
+
+ if (envp != NULL)
+ envp_to_use = envp;
+ else
+ envp_to_use = environ;
+
+ if (stdin != NULL) {
+ if (pipe (stdin_pipe) != 0)
+ goto out;
+ }
+
+ if (stdout != NULL) {
+ if (pipe (stdout_pipe) != 0)
+ goto out;
+ }
+
+ if (stderr != NULL) {
+ if (pipe (stderr_pipe) != 0)
+ goto out;
+ }
+
+ pid = fork ();
+ if (pid == -1)
+ goto out;
+
+ if (pid == 0) {
+ /* child */
+
+ signal (SIGPIPE, SIG_DFL);
+
+ /* close unused ends */
+ if (stdin_pipe[1] != -1) {
+ close (stdin_pipe[1]);
+ }
+ if (stdout_pipe[0] != -1) {
+ close (stdout_pipe[0]);
+ }
+ if (stderr_pipe[0] != -1) {
+ close (stderr_pipe[0]);
+ }
+
+ /* close all open file descriptors of child except stdin, stdout, stderr */
+ _fdwalk (set_close_on_exec, (void *) 3);
+
+ /* change working directory */
+ if (working_directory != NULL) {
+ if (chdir (working_directory) != 0) {
+ exit (128 + errno);
+ }
+ }
+
+ /* set stdin, stdout and stderr */
+
+ if (stdin == NULL) {
+ int fd_null;
+ fd_null = open ("/dev/null", O_RDONLY);
+ if (fd_null < 0) {
+ exit (128 + errno);
+ }
+ if (_sane_dup2 (fd_null, 0) < 0) {
+ exit (128 + errno);
+ }
+ } else {
+ if (_sane_dup2 (stdin_pipe[0], 0) < 0) {
+ exit (128 + errno);
+ }
+ }
+ if (stdout != NULL) {
+ if (_sane_dup2 (stdout_pipe[1], 1) < 0) {
+ exit (128 + errno);
+ }
+ }
+
+ if (stderr != NULL) {
+ if (_sane_dup2 (stderr_pipe[1], 2) < 0) {
+ exit (128 + errno);
+ }
+ }
+
+ /* finally, execute the child */
+ if (execve (argv[0], argv, envp_to_use) == -1) {
+ exit (128 + errno);
+ }
+
+ } else {
+ char *wp;
+
+ /* parent */
+
+ /* closed unused ends */
+ if (stdin_pipe[0] != -1) {
+ close (stdin_pipe[0]);
+ }
+ if (stdout_pipe[1] != -1) {
+ close (stdout_pipe[1]);
+ }
+ if (stderr_pipe[1] != -1) {
+ close (stderr_pipe[1]);
+ }
+
+ wp = stdin;
+
+ while (stdin_pipe[1] != -1 || stdout_pipe[0] != -1 || stderr_pipe[0] != -1) {
+ int ret;
+ ssize_t num_read;
+ ssize_t num_written;
+ int max;
+ fd_set read_fds;
+ fd_set write_fds;
+
+ FD_ZERO (&read_fds);
+ FD_ZERO (&write_fds);
+ if (stdin_pipe[1] != -1) {
+ FD_SET (stdin_pipe[1], &write_fds);
+ }
+ if (stdout_pipe[0] != -1) {
+ FD_SET (stdout_pipe[0], &read_fds);
+ }
+ if (stderr_pipe[0] != -1) {
+ FD_SET (stderr_pipe[0], &read_fds);
+ }
+
+ max = stdin_pipe[1];
+ if (stdout_pipe[0] > max)
+ max = stdout_pipe[0];
+ if (stderr_pipe[0] > max)
+ max = stderr_pipe[0];
+
+ ret = select (max + 1,
+ &read_fds,
+ &write_fds,
+ NULL,
+ NULL);
+
+ if (ret < 0 && errno != EINTR) {
+ goto out;
+ }
+
+ if (stdin_pipe[1] != -1) {
+ num_written = _write_to (stdin_pipe[1], wp);
+
+ if (num_written == -1) {
+ goto out;
+ }
+
+ wp += num_written;
+ if (*wp == '\0') {
+ close (stdin_pipe[1]);
+ stdin_pipe[1] = -1;
+ }
+ }
+
+ if (stdout_pipe[0] != -1) {
+ num_read = _read_from (stdout_pipe[0], stdout);
+ if (num_read == 0) {
+ close (stdout_pipe[0]);
+ stdout_pipe[0] = -1;
+ } else if (num_read == -1) {
+ goto out;
+ }
+ }
+
+ if (stderr_pipe[0] != -1) {
+ num_read = _read_from (stderr_pipe[0], stderr);
+ if (num_read == 0) {
+ close (stderr_pipe[0]);
+ stderr_pipe[0] = -1;
+ } else if (num_read == -1) {
+ goto out;
+ }
+ }
+ }
+
+ if (waitpid (pid, out_exit_status, 0) == -1) {
+ goto out;
+ }
+ pid = -1;
+ }
+
+ //kit_debug ("exit %d", WEXITSTATUS (*out_exit_status));
+
+ if (WEXITSTATUS (*out_exit_status) < 128) {
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ errno = WEXITSTATUS (*out_exit_status) - 128;
+ }
+
+out:
+ if (pid != -1) {
+ kill (pid, SIGKILL);
+ waitpid (pid, out_exit_status, 0);
+ }
+
+ if (stdin_pipe[1] != -1)
+ close (stdin_pipe[1]);
+ if (stdout_pipe[0] != -1)
+ close (stdout_pipe[0]);
+ if (stderr_pipe[0] != -1)
+ close (stderr_pipe[0]);
+
+ if (!ret) {
+ if (stdout != NULL) {
+ kit_free (*stdout);
+ *stdout = NULL;
+ }
+ if (stderr != NULL) {
+ kit_free (*stderr);
+ *stderr = NULL;
+ }
+ }
+
+ return ret;
+
+}
+
+
+#ifdef KIT_BUILD_TESTS
+
+static kit_bool_t
+_run_test (void)
+{
+ char path[] = "/tmp/kit-spawn-test";
+ char *script1 =
+ "#!/bin/sh" "\n"
+ "echo \"Hello World\"" "\n"
+ "echo \"Goodbye World\" 1>&2" "\n"
+ "exit 42" "\n";
+ char *script2 =
+ "#!/bin/sh" "\n"
+ "exit 43" "\n";
+ char *script3 =
+ "#!/bin/sh" "\n"
+ "echo -n \"$KIT_TEST_VAR\"" "\n"
+ "exit 0" "\n";
+ char *script4 =
+ "#!/bin/sh" "\n"
+ "if [ \"x$KIT_TEST_VAR\" = \"x\" ] ; then" "\n"
+ " exit 0" "\n"
+ "fi" "\n"
+ "exit 1" "\n";
+ char *script5 =
+ "#!/bin/sh" "\n"
+ "pwd" "\n"
+ "exit 0" "\n";
+ char *script6 =
+ "#!/bin/sh" "\n"
+ "read value" "\n"
+ "echo -n \"$value\"" "\n"
+ "echo -n \" \"" "\n"
+ "read value" "\n"
+ "echo -n \"$value\"" "\n"
+ "exit 0" "\n";
+ char *argv[] = {"/tmp/kit-spawn-test", NULL};
+ char *stdout;
+ char *stderr;
+ int exit_status;
+ struct stat statbuf;
+
+ /* script echoing to stdout and stderr */
+ if (kit_file_set_contents (path, 0700, script1, strlen (script1))) {
+ if (kit_spawn_sync ("/",
+ argv,
+ NULL,
+ NULL,
+ &stdout,
+ &stderr,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 42);
+ kit_assert (stdout != NULL && strcmp (stdout, "Hello World\n") == 0);
+ kit_assert (stderr != NULL && strcmp (stderr, "Goodbye World\n") == 0);
+ kit_free (stdout);
+ kit_free (stderr);
+ }
+
+ if (kit_spawn_sync ("/",
+ argv,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 42);
+ }
+
+ kit_assert (unlink (path) == 0);
+ }
+
+ /* silent script */
+ if (kit_file_set_contents (path, 0700, script2, strlen (script2))) {
+ if (kit_spawn_sync ("/",
+ argv,
+ NULL,
+ NULL,
+ &stdout,
+ &stderr,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 43);
+ kit_assert (stdout == NULL);
+ kit_assert (stderr == NULL);
+ }
+
+ kit_assert (unlink (path) == 0);
+ }
+
+ /* check environment is set */
+ if (kit_file_set_contents (path, 0700, script3, strlen (script3))) {
+ char *envp[] = {"KIT_TEST_VAR=some_value", NULL};
+
+ if (kit_spawn_sync ("/",
+ argv,
+ envp,
+ NULL,
+ &stdout,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 0);
+ kit_assert (stdout != NULL && strcmp (stdout, "some_value") == 0);
+ kit_free (stdout);
+ }
+
+ kit_assert (unlink (path) == 0);
+ }
+
+ /* check environment is replaced */
+ if (kit_file_set_contents (path, 0700, script4, strlen (script4))) {
+ char *envp[] = {NULL};
+
+ kit_assert (setenv ("KIT_TEST_VAR", "foobar", 1) == 0);
+
+ if (kit_spawn_sync ("/",
+ argv,
+ envp,
+ NULL,
+ NULL,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 0);
+ }
+
+ kit_assert (unlink (path) == 0);
+ kit_assert (unsetenv ("KIT_TEST_VAR") == 0);
+ }
+
+ /* check working directory */
+ if (kit_file_set_contents (path, 0700, script5, strlen (script5))) {
+ kit_assert (stat ("/tmp", &statbuf) == 0 && S_ISDIR (statbuf.st_mode));
+ if (kit_spawn_sync ("/tmp",
+ argv,
+ NULL,
+ NULL,
+ &stdout,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 0);
+ kit_assert (stdout != NULL && strcmp (stdout, "/tmp\n") == 0);
+ kit_free (stdout);
+ }
+
+ kit_assert (stat ("/usr", &statbuf) == 0 && S_ISDIR (statbuf.st_mode));
+ if (kit_spawn_sync ("/usr",
+ argv,
+ NULL,
+ NULL,
+ &stdout,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 0);
+ kit_assert (stdout != NULL && strcmp (stdout, "/usr\n") == 0);
+ kit_free (stdout);
+ }
+
+ kit_assert (unlink (path) == 0);
+ }
+
+ /* check bogus working directory */
+ kit_assert (stat ("/org/freedesktop/PolicyKit/bogus-fs-path", &statbuf) != 0);
+ kit_assert (kit_spawn_sync ("/org/freedesktop/PolicyKit/bogus-fs-path",
+ argv,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &exit_status) == FALSE &&
+ (errno == ENOENT || errno == ENOMEM));
+
+ /* check for writing to stdin */
+ if (kit_file_set_contents (path, 0700, script6, strlen (script6))) {
+ if (kit_spawn_sync (NULL,
+ argv,
+ NULL,
+ "foobar0\nfoobar1",
+ &stdout,
+ NULL,
+ &exit_status)) {
+ kit_assert (WEXITSTATUS (exit_status) == 0);
+ kit_assert (stdout != NULL && strcmp (stdout, "foobar0 foobar1") == 0);
+ kit_free (stdout);
+ }
+
+ kit_assert (unlink (path) == 0);
+ }
+
+ return TRUE;
+}
+
+KitTest _test_spawn = {
+ "kit_spawn",
+ NULL,
+ NULL,
+ _run_test
+};
+
+#endif /* KIT_BUILD_TESTS */
diff --git a/src/kit/kit-spawn.h b/src/kit/kit-spawn.h
new file mode 100644
index 0000000..72b76f7
--- /dev/null
+++ b/src/kit/kit-spawn.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/***************************************************************************
+ *
+ * kit-spawn.h : Spawn utilities
+ *
+ * Copyright (C) 2007 David Zeuthen, <david at fubar.dk>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ **************************************************************************/
+
+#if !defined (KIT_COMPILATION) && !defined(_KIT_INSIDE_KIT_H)
+#error "Only <kit/kit.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef KIT_SPAWN_H
+#define KIT_SPAWN_H
+
+#include <kit/kit.h>
+
+KIT_BEGIN_DECLS
+
+kit_bool_t kit_spawn_sync (const char *working_directory,
+ char **argv,
+ char **envp,
+ char *stdin,
+ char **stdout,
+ char **stderr,
+ int *out_exit_status);
+
+KIT_END_DECLS
+
+#endif /* KIT_SPAWN_H */
+
+
More information about the hal-commit
mailing list