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