summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2006-08-30 01:06:28 +0000
committerHavoc Pennington <hp@redhat.com>2006-08-30 01:06:28 +0000
commit338c96b4c69c9d870f41aa6d836338d89e6ed9c3 (patch)
treea8f0bf35a35c4fb8680c813f764aab870dcb2d50
parentc904d4d5010aff2b33198069d7256c4cfdfff15e (diff)
2006-08-29 Havoc Pennington <hp@redhat.com>
* dbus/dbus-sysdeps-util.c, dbus/dbus-sysdeps-util-unix.c: change from Ralf Habacker to move UNIX-specific sysdeps into a separate file.
-rw-r--r--ChangeLog5
-rw-r--r--dbus/Makefile.am1
-rw-r--r--dbus/dbus-sysdeps-util-unix.c740
-rw-r--r--dbus/dbus-sysdeps-util.c715
4 files changed, 747 insertions, 714 deletions
diff --git a/ChangeLog b/ChangeLog
index 4cf48b71..203584c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2006-08-29 Havoc Pennington <hp@redhat.com>
+
+ * dbus/dbus-sysdeps-util.c, dbus/dbus-sysdeps-util-unix.c: change
+ from Ralf Habacker to move UNIX-specific sysdeps into a separate file.
+
2006-08-25 John (J5) Palmieri <johnp@redhat.com>
* test/Makefile.am: change find to use syntax that works with non
diff --git a/dbus/Makefile.am b/dbus/Makefile.am
index c3aab810..2fdcf385 100644
--- a/dbus/Makefile.am
+++ b/dbus/Makefile.am
@@ -129,6 +129,7 @@ DBUS_UTIL_SOURCES= \
dbus-spawn.h \
dbus-string-util.c \
dbus-sysdeps-util.c \
+ dbus-sysdeps-util-unix.c \
dbus-test.c \
dbus-test.h \
dbus-userdb-util.c
diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c
new file mode 100644
index 00000000..404be44b
--- /dev/null
+++ b/dbus/dbus-sysdeps-util-unix.c
@@ -0,0 +1,740 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-sysdeps-util-unix.c Would be in dbus-sysdeps-unix.c, but not used in libdbus
+ *
+ * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "dbus-sysdeps.h"
+#include "dbus-internals.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
+#define DBUS_USERDB_INCLUDES_PRIVATE 1
+#include "dbus-userdb.h"
+#include "dbus-test.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <dirent.h>
+#include <sys/un.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/**
+ * @addtogroup DBusInternalsUtils
+ * @{
+ */
+
+/**
+ * Does the chdir, fork, setsid, etc. to become a daemon process.
+ *
+ * @param pidfile #NULL, or pidfile to create
+ * @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_become_daemon (const DBusString *pidfile,
+ int print_pid_fd,
+ DBusError *error)
+{
+ const char *s;
+ pid_t child_pid;
+ int dev_null_fd;
+
+ _dbus_verbose ("Becoming a daemon...\n");
+
+ _dbus_verbose ("chdir to /\n");
+ if (chdir ("/") < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not chdir() to root directory");
+ return FALSE;
+ }
+
+ _dbus_verbose ("forking...\n");
+ switch ((child_pid = fork ()))
+ {
+ case -1:
+ _dbus_verbose ("fork failed\n");
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to fork daemon: %s", _dbus_strerror (errno));
+ return FALSE;
+ break;
+
+ case 0:
+ _dbus_verbose ("in child, closing std file descriptors\n");
+
+ /* silently ignore failures here, if someone
+ * doesn't have /dev/null we may as well try
+ * to continue anyhow
+ */
+
+ dev_null_fd = open ("/dev/null", O_RDWR);
+ if (dev_null_fd >= 0)
+ {
+ dup2 (dev_null_fd, 0);
+ dup2 (dev_null_fd, 1);
+
+ s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
+ if (s == NULL || *s == '\0')
+ dup2 (dev_null_fd, 2);
+ else
+ _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
+ }
+
+ /* Get a predictable umask */
+ _dbus_verbose ("setting umask\n");
+ umask (022);
+ break;
+
+ default:
+ if (pidfile)
+ {
+ _dbus_verbose ("parent writing pid file\n");
+ if (!_dbus_write_pid_file (pidfile,
+ child_pid,
+ error))
+ {
+ _dbus_verbose ("pid file write failed, killing child\n");
+ kill (child_pid, SIGTERM);
+ return FALSE;
+ }
+ }
+
+ /* Write PID if requested */
+ if (print_pid_fd >= 0)
+ {
+ DBusString pid;
+ int bytes;
+
+ if (!_dbus_string_init (&pid))
+ {
+ _DBUS_SET_OOM (error);
+ kill (child_pid, SIGTERM);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append_int (&pid, child_pid) ||
+ !_dbus_string_append (&pid, "\n"))
+ {
+ _dbus_string_free (&pid);
+ _DBUS_SET_OOM (error);
+ kill (child_pid, SIGTERM);
+ return FALSE;
+ }
+
+ bytes = _dbus_string_get_length (&pid);
+ if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Printing message bus PID: %s\n",
+ _dbus_strerror (errno));
+ _dbus_string_free (&pid);
+ kill (child_pid, SIGTERM);
+ return FALSE;
+ }
+
+ _dbus_string_free (&pid);
+ }
+ _dbus_verbose ("parent exiting\n");
+ _exit (0);
+ break;
+ }
+
+ _dbus_verbose ("calling setsid()\n");
+ if (setsid () == -1)
+ _dbus_assert_not_reached ("setsid() failed");
+
+ return TRUE;
+}
+
+
+/**
+ * Creates a file containing the process ID.
+ *
+ * @param filename the filename to write to
+ * @param pid our process ID
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_write_pid_file (const DBusString *filename,
+ unsigned long pid,
+ DBusError *error)
+{
+ const char *cfilename;
+ int fd;
+ FILE *f;
+
+ cfilename = _dbus_string_get_const_data (filename);
+
+ fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
+
+ if (fd < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to open \"%s\": %s", cfilename,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if ((f = fdopen (fd, "w")) == NULL)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
+ close (fd);
+ return FALSE;
+ }
+
+ if (fprintf (f, "%lu\n", pid) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to write to \"%s\": %s", cfilename,
+ _dbus_strerror (errno));
+
+ fclose (f);
+ return FALSE;
+ }
+
+ if (fclose (f) == EOF)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to close \"%s\": %s", cfilename,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * Changes the user and group the bus is running as.
+ *
+ * @param uid the new user ID
+ * @param gid the new group ID
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_change_identity (dbus_uid_t uid,
+ dbus_gid_t gid,
+ DBusError *error)
+{
+ /* setgroups() only works if we are a privileged process,
+ * so we don't return error on failure; the only possible
+ * failure is that we don't have perms to do it.
+ * FIXME not sure this is right, maybe if setuid()
+ * is going to work then setgroups() should also work.
+ */
+ if (setgroups (0, NULL) < 0)
+ _dbus_warn ("Failed to drop supplementary groups: %s\n",
+ _dbus_strerror (errno));
+
+ /* Set GID first, or the setuid may remove our permission
+ * to change the GID
+ */
+ if (setgid (gid) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set GID to %lu: %s", gid,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if (setuid (uid) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set UID to %lu: %s", uid,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** Installs a UNIX signal handler
+ *
+ * @param sig the signal to handle
+ * @param handler the handler
+ */
+void
+_dbus_set_signal_handler (int sig,
+ DBusSignalHandler handler)
+{
+ struct sigaction act;
+ sigset_t empty_mask;
+
+ sigemptyset (&empty_mask);
+ act.sa_handler = handler;
+ act.sa_mask = empty_mask;
+ act.sa_flags = 0;
+ sigaction (sig, &act, NULL);
+}
+
+
+/**
+ * Removes a directory; Directory must be empty
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_delete_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (rmdir (filename_c) != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to remove directory %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** Checks if a file exists
+*
+* @param file full path to the file
+* @returns #TRUE if file exists
+*/
+dbus_bool_t
+_dbus_file_exists (const char *file)
+{
+ return (access (file, F_OK) == 0);
+}
+
+/** Checks if user is at the console
+*
+* @param username user to check
+* @param error return location for errors
+* @returns #TRUE is the user is at the consolei and there are no errors
+*/
+dbus_bool_t
+_dbus_user_at_console (const char *username,
+ DBusError *error)
+{
+
+ DBusString f;
+ dbus_bool_t result;
+
+ result = FALSE;
+ if (!_dbus_string_init (&f))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+
+ if (!_dbus_string_append (&f, username))
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+
+ result = _dbus_file_exists (_dbus_string_get_const_data (&f));
+
+ out:
+ _dbus_string_free (&f);
+
+ return result;
+}
+
+
+/**
+ * Checks whether the filename is an absolute path
+ *
+ * @param filename the filename
+ * @returns #TRUE if an absolute path
+ */
+dbus_bool_t
+_dbus_path_is_absolute (const DBusString *filename)
+{
+ if (_dbus_string_get_length (filename) > 0)
+ return _dbus_string_get_byte (filename, 0) == '/';
+ else
+ return FALSE;
+}
+
+/**
+ * stat() wrapper.
+ *
+ * @param filename the filename to stat
+ * @param statbuf the stat info to fill in
+ * @param error return location for error
+ * @returns #FALSE if error was set
+ */
+dbus_bool_t
+_dbus_stat (const DBusString *filename,
+ DBusStat *statbuf,
+ DBusError *error)
+{
+ const char *filename_c;
+ struct stat sb;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (stat (filename_c, &sb) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ statbuf->mode = sb.st_mode;
+ statbuf->nlink = sb.st_nlink;
+ statbuf->uid = sb.st_uid;
+ statbuf->gid = sb.st_gid;
+ statbuf->size = sb.st_size;
+ statbuf->atime = sb.st_atime;
+ statbuf->mtime = sb.st_mtime;
+ statbuf->ctime = sb.st_ctime;
+
+ return TRUE;
+}
+
+
+/**
+ * Internals of directory iterator
+ */
+struct DBusDirIter
+{
+ DIR *d; /**< The DIR* from opendir() */
+
+};
+
+/**
+ * Open a directory to iterate over.
+ *
+ * @param filename the directory name
+ * @param error exception return object or #NULL
+ * @returns new iterator, or #NULL on error
+ */
+DBusDirIter*
+_dbus_directory_open (const DBusString *filename,
+ DBusError *error)
+{
+ DIR *d;
+ DBusDirIter *iter;
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ d = opendir (filename_c);
+ if (d == NULL)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to read directory \"%s\": %s",
+ filename_c,
+ _dbus_strerror (errno));
+ return NULL;
+ }
+ iter = dbus_new0 (DBusDirIter, 1);
+ if (iter == NULL)
+ {
+ closedir (d);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Could not allocate memory for directory iterator");
+ return NULL;
+ }
+
+ iter->d = d;
+
+ return iter;
+}
+
+/**
+ * Get next file in the directory. Will not return "." or ".." on
+ * UNIX. If an error occurs, the contents of "filename" are
+ * undefined. The error is never set if the function succeeds.
+ *
+ * @todo for thread safety, I think we have to use
+ * readdir_r(). (GLib has the same issue, should file a bug.)
+ *
+ * @param iter the iterator
+ * @param filename string to be set to the next file in the dir
+ * @param error return location for error
+ * @returns #TRUE if filename was filled in with a new filename
+ */
+dbus_bool_t
+_dbus_directory_get_next_file (DBusDirIter *iter,
+ DBusString *filename,
+ DBusError *error)
+{
+ struct dirent *ent;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ again:
+ errno = 0;
+ ent = readdir (iter->d);
+ if (ent == NULL)
+ {
+ if (errno != 0)
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
+ return FALSE;
+ }
+ else if (ent->d_name[0] == '.' &&
+ (ent->d_name[1] == '\0' ||
+ (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
+ goto again;
+ else
+ {
+ _dbus_string_set_length (filename, 0);
+ if (!_dbus_string_append (filename, ent->d_name))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "No memory to read directory entry");
+ return FALSE;
+ }
+ else
+ return TRUE;
+ }
+}
+
+/**
+ * Closes a directory iteration.
+ */
+void
+_dbus_directory_close (DBusDirIter *iter)
+{
+ closedir (iter->d);
+ dbus_free (iter);
+}
+
+static dbus_bool_t
+fill_user_info_from_group (struct group *g,
+ DBusGroupInfo *info,
+ DBusError *error)
+{
+ _dbus_assert (g->gr_name != NULL);
+
+ info->gid = g->gr_gid;
+ info->groupname = _dbus_strdup (g->gr_name);
+
+ /* info->members = dbus_strdupv (g->gr_mem) */
+
+ if (info->groupname == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+fill_group_info (DBusGroupInfo *info,
+ dbus_gid_t gid,
+ const DBusString *groupname,
+ DBusError *error)
+{
+ const char *group_c_str;
+
+ _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
+ _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
+
+ if (groupname)
+ group_c_str = _dbus_string_get_const_data (groupname);
+ else
+ group_c_str = NULL;
+
+ /* For now assuming that the getgrnam() and getgrgid() flavors
+ * always correspond to the pwnam flavors, if not we have
+ * to add more configure checks.
+ */
+
+#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
+ {
+ struct group *g;
+ int result;
+ char buf[1024];
+ struct group g_str;
+
+ g = NULL;
+#ifdef HAVE_POSIX_GETPWNAM_R
+
+ if (group_c_str)
+ result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
+ &g);
+ else
+ result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
+ &g);
+#else
+ g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
+ result = 0;
+#endif /* !HAVE_POSIX_GETPWNAM_R */
+ if (result == 0 && g == &g_str)
+ {
+ return fill_user_info_from_group (g, info, error);
+ }
+ else
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Group %s unknown or failed to look it up\n",
+ group_c_str ? group_c_str : "???");
+ return FALSE;
+ }
+ }
+#else /* ! HAVE_GETPWNAM_R */
+ {
+ /* I guess we're screwed on thread safety here */
+ struct group *g;
+
+ g = getgrnam (group_c_str);
+
+ if (g != NULL)
+ {
+ return fill_user_info_from_group (g, info, error);
+ }
+ else
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Group %s unknown or failed to look it up\n",
+ group_c_str ? group_c_str : "???");
+ return FALSE;
+ }
+ }
+#endif /* ! HAVE_GETPWNAM_R */
+}
+
+/**
+ * Initializes the given DBusGroupInfo struct
+ * with information about the given group name.
+ *
+ * @param info the group info struct
+ * @param groupname name of group
+ * @param error the error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_group_info_fill (DBusGroupInfo *info,
+ const DBusString *groupname,
+ DBusError *error)
+{
+ return fill_group_info (info, DBUS_GID_UNSET,
+ groupname, error);
+
+}
+
+/**
+ * Initializes the given DBusGroupInfo struct
+ * with information about the given group ID.
+ *
+ * @param info the group info struct
+ * @param gid group ID
+ * @param error the error return
+ * @returns #FALSE if error is set
+ */
+dbus_bool_t
+_dbus_group_info_fill_gid (DBusGroupInfo *info,
+ dbus_gid_t gid,
+ DBusError *error)
+{
+ return fill_group_info (info, gid, NULL, error);
+}
+
+/** @} */ /* End of DBusInternalsUtils functions */
+
+/**
+ * @addtogroup DBusString
+ *
+ * @{
+ */
+/**
+ * Get the directory name from a complete filename
+ * @param filename the filename
+ * @param dirname string to append directory name to
+ * @returns #FALSE if no memory
+ */
+dbus_bool_t
+_dbus_string_get_dirname (const DBusString *filename,
+ DBusString *dirname)
+{
+ int sep;
+
+ _dbus_assert (filename != dirname);
+ _dbus_assert (filename != NULL);
+ _dbus_assert (dirname != NULL);
+
+ /* Ignore any separators on the end */
+ sep = _dbus_string_get_length (filename);
+ if (sep == 0)
+ return _dbus_string_append (dirname, "."); /* empty string passed in */
+
+ while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
+ --sep;
+
+ _dbus_assert (sep >= 0);
+
+ if (sep == 0)
+ return _dbus_string_append (dirname, "/");
+
+ /* Now find the previous separator */
+ _dbus_string_find_byte_backward (filename, sep, '/', &sep);
+ if (sep < 0)
+ return _dbus_string_append (dirname, ".");
+
+ /* skip multiple separators */
+ while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
+ --sep;
+
+ _dbus_assert (sep >= 0);
+
+ if (sep == 0 &&
+ _dbus_string_get_byte (filename, 0) == '/')
+ return _dbus_string_append (dirname, "/");
+ else
+ return _dbus_string_copy_len (filename, 0, sep - 0,
+ dirname, _dbus_string_get_length (dirname));
+}
+/** @} */ /* DBusString stuff */
+
diff --git a/dbus/dbus-sysdeps-util.c b/dbus/dbus-sysdeps-util.c
index ebe20b4a..698b893e 100644
--- a/dbus/dbus-sysdeps-util.c
+++ b/dbus/dbus-sysdeps-util.c
@@ -1,5 +1,5 @@
/* -*- mode: C; c-file-style: "gnu" -*- */
-/* dbus-sysdeps-util.c Would be in dbus-sysdeps.c, but not used in libdbus
+/* dbus-sysdeps-util.c Tests for dbus-sysdeps.h API
*
* Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
* Copyright (C) 2003 CodeFactory AB
@@ -23,722 +23,9 @@
*/
#include "dbus-sysdeps.h"
#include "dbus-internals.h"
-#include "dbus-protocol.h"
#include "dbus-string.h"
-#define DBUS_USERDB_INCLUDES_PRIVATE 1
-#include "dbus-userdb.h"
#include "dbus-test.h"
-#include <sys/types.h>
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <grp.h>
-#include <sys/socket.h>
-#include <dirent.h>
-#include <sys/un.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-/**
- * @addtogroup DBusInternalsUtils
- * @{
- */
-
-/**
- * Does the chdir, fork, setsid, etc. to become a daemon process.
- *
- * @param pidfile #NULL, or pidfile to create
- * @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
- * @param error return location for errors
- * @returns #FALSE on failure
- */
-dbus_bool_t
-_dbus_become_daemon (const DBusString *pidfile,
- int print_pid_fd,
- DBusError *error)
-{
- const char *s;
- pid_t child_pid;
- int dev_null_fd;
-
- _dbus_verbose ("Becoming a daemon...\n");
-
- _dbus_verbose ("chdir to /\n");
- if (chdir ("/") < 0)
- {
- dbus_set_error (error, DBUS_ERROR_FAILED,
- "Could not chdir() to root directory");
- return FALSE;
- }
-
- _dbus_verbose ("forking...\n");
- switch ((child_pid = fork ()))
- {
- case -1:
- _dbus_verbose ("fork failed\n");
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to fork daemon: %s", _dbus_strerror (errno));
- return FALSE;
- break;
-
- case 0:
- _dbus_verbose ("in child, closing std file descriptors\n");
-
- /* silently ignore failures here, if someone
- * doesn't have /dev/null we may as well try
- * to continue anyhow
- */
-
- dev_null_fd = open ("/dev/null", O_RDWR);
- if (dev_null_fd >= 0)
- {
- dup2 (dev_null_fd, 0);
- dup2 (dev_null_fd, 1);
-
- s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
- if (s == NULL || *s == '\0')
- dup2 (dev_null_fd, 2);
- else
- _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
- }
-
- /* Get a predictable umask */
- _dbus_verbose ("setting umask\n");
- umask (022);
- break;
-
- default:
- if (pidfile)
- {
- _dbus_verbose ("parent writing pid file\n");
- if (!_dbus_write_pid_file (pidfile,
- child_pid,
- error))
- {
- _dbus_verbose ("pid file write failed, killing child\n");
- kill (child_pid, SIGTERM);
- return FALSE;
- }
- }
-
- /* Write PID if requested */
- if (print_pid_fd >= 0)
- {
- DBusString pid;
- int bytes;
-
- if (!_dbus_string_init (&pid))
- {
- _DBUS_SET_OOM (error);
- kill (child_pid, SIGTERM);
- return FALSE;
- }
-
- if (!_dbus_string_append_int (&pid, child_pid) ||
- !_dbus_string_append (&pid, "\n"))
- {
- _dbus_string_free (&pid);
- _DBUS_SET_OOM (error);
- kill (child_pid, SIGTERM);
- return FALSE;
- }
-
- bytes = _dbus_string_get_length (&pid);
- if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
- {
- dbus_set_error (error, DBUS_ERROR_FAILED,
- "Printing message bus PID: %s\n",
- _dbus_strerror (errno));
- _dbus_string_free (&pid);
- kill (child_pid, SIGTERM);
- return FALSE;
- }
-
- _dbus_string_free (&pid);
- }
- _dbus_verbose ("parent exiting\n");
- _exit (0);
- break;
- }
-
- _dbus_verbose ("calling setsid()\n");
- if (setsid () == -1)
- _dbus_assert_not_reached ("setsid() failed");
-
- return TRUE;
-}
-
-
-/**
- * Creates a file containing the process ID.
- *
- * @param filename the filename to write to
- * @param pid our process ID
- * @param error return location for errors
- * @returns #FALSE on failure
- */
-dbus_bool_t
-_dbus_write_pid_file (const DBusString *filename,
- unsigned long pid,
- DBusError *error)
-{
- const char *cfilename;
- int fd;
- FILE *f;
-
- cfilename = _dbus_string_get_const_data (filename);
-
- fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
-
- if (fd < 0)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to open \"%s\": %s", cfilename,
- _dbus_strerror (errno));
- return FALSE;
- }
-
- if ((f = fdopen (fd, "w")) == NULL)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
- close (fd);
- return FALSE;
- }
-
- if (fprintf (f, "%lu\n", pid) < 0)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to write to \"%s\": %s", cfilename,
- _dbus_strerror (errno));
-
- fclose (f);
- return FALSE;
- }
-
- if (fclose (f) == EOF)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to close \"%s\": %s", cfilename,
- _dbus_strerror (errno));
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-/**
- * Changes the user and group the bus is running as.
- *
- * @param uid the new user ID
- * @param gid the new group ID
- * @param error return location for errors
- * @returns #FALSE on failure
- */
-dbus_bool_t
-_dbus_change_identity (dbus_uid_t uid,
- dbus_gid_t gid,
- DBusError *error)
-{
- /* setgroups() only works if we are a privileged process,
- * so we don't return error on failure; the only possible
- * failure is that we don't have perms to do it.
- * FIXME not sure this is right, maybe if setuid()
- * is going to work then setgroups() should also work.
- */
- if (setgroups (0, NULL) < 0)
- _dbus_warn ("Failed to drop supplementary groups: %s\n",
- _dbus_strerror (errno));
-
- /* Set GID first, or the setuid may remove our permission
- * to change the GID
- */
- if (setgid (gid) < 0)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to set GID to %lu: %s", gid,
- _dbus_strerror (errno));
- return FALSE;
- }
-
- if (setuid (uid) < 0)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to set UID to %lu: %s", uid,
- _dbus_strerror (errno));
- return FALSE;
- }
-
- return TRUE;
-}
-
-/** Installs a UNIX signal handler
- *
- * @param sig the signal to handle
- * @param handler the handler
- */
-void
-_dbus_set_signal_handler (int sig,
- DBusSignalHandler handler)
-{
- struct sigaction act;
- sigset_t empty_mask;
-
- sigemptyset (&empty_mask);
- act.sa_handler = handler;
- act.sa_mask = empty_mask;
- act.sa_flags = 0;
- sigaction (sig, &act, NULL);
-}
-
-
-/**
- * Removes a directory; Directory must be empty
- *
- * @param filename directory filename
- * @param error initialized error object
- * @returns #TRUE on success
- */
-dbus_bool_t
-_dbus_delete_directory (const DBusString *filename,
- DBusError *error)
-{
- const char *filename_c;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- filename_c = _dbus_string_get_const_data (filename);
-
- if (rmdir (filename_c) != 0)
- {
- dbus_set_error (error, DBUS_ERROR_FAILED,
- "Failed to remove directory %s: %s\n",
- filename_c, _dbus_strerror (errno));
- return FALSE;
- }
-
- return TRUE;
-}
-
-/** Checks if a file exists
-*
-* @param file full path to the file
-* @returns #TRUE if file exists
-*/
-dbus_bool_t
-_dbus_file_exists (const char *file)
-{
- return (access (file, F_OK) == 0);
-}
-
-/** Checks if user is at the console
-*
-* @param username user to check
-* @param error return location for errors
-* @returns #TRUE is the user is at the consolei and there are no errors
-*/
-dbus_bool_t
-_dbus_user_at_console (const char *username,
- DBusError *error)
-{
-
- DBusString f;
- dbus_bool_t result;
-
- result = FALSE;
- if (!_dbus_string_init (&f))
- {
- _DBUS_SET_OOM (error);
- return FALSE;
- }
-
- if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
- {
- _DBUS_SET_OOM (error);
- goto out;
- }
-
-
- if (!_dbus_string_append (&f, username))
- {
- _DBUS_SET_OOM (error);
- goto out;
- }
-
- result = _dbus_file_exists (_dbus_string_get_const_data (&f));
-
- out:
- _dbus_string_free (&f);
-
- return result;
-}
-
-
-/**
- * Checks whether the filename is an absolute path
- *
- * @param filename the filename
- * @returns #TRUE if an absolute path
- */
-dbus_bool_t
-_dbus_path_is_absolute (const DBusString *filename)
-{
- if (_dbus_string_get_length (filename) > 0)
- return _dbus_string_get_byte (filename, 0) == '/';
- else
- return FALSE;
-}
-
-/**
- * stat() wrapper.
- *
- * @param filename the filename to stat
- * @param statbuf the stat info to fill in
- * @param error return location for error
- * @returns #FALSE if error was set
- */
-dbus_bool_t
-_dbus_stat (const DBusString *filename,
- DBusStat *statbuf,
- DBusError *error)
-{
- const char *filename_c;
- struct stat sb;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- filename_c = _dbus_string_get_const_data (filename);
-
- if (stat (filename_c, &sb) < 0)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "%s", _dbus_strerror (errno));
- return FALSE;
- }
-
- statbuf->mode = sb.st_mode;
- statbuf->nlink = sb.st_nlink;
- statbuf->uid = sb.st_uid;
- statbuf->gid = sb.st_gid;
- statbuf->size = sb.st_size;
- statbuf->atime = sb.st_atime;
- statbuf->mtime = sb.st_mtime;
- statbuf->ctime = sb.st_ctime;
-
- return TRUE;
-}
-
-
-/**
- * Internals of directory iterator
- */
-struct DBusDirIter
-{
- DIR *d; /**< The DIR* from opendir() */
-
-};
-
-/**
- * Open a directory to iterate over.
- *
- * @param filename the directory name
- * @param error exception return object or #NULL
- * @returns new iterator, or #NULL on error
- */
-DBusDirIter*
-_dbus_directory_open (const DBusString *filename,
- DBusError *error)
-{
- DIR *d;
- DBusDirIter *iter;
- const char *filename_c;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- filename_c = _dbus_string_get_const_data (filename);
-
- d = opendir (filename_c);
- if (d == NULL)
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Failed to read directory \"%s\": %s",
- filename_c,
- _dbus_strerror (errno));
- return NULL;
- }
- iter = dbus_new0 (DBusDirIter, 1);
- if (iter == NULL)
- {
- closedir (d);
- dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
- "Could not allocate memory for directory iterator");
- return NULL;
- }
-
- iter->d = d;
-
- return iter;
-}
-
-/**
- * Get next file in the directory. Will not return "." or ".." on
- * UNIX. If an error occurs, the contents of "filename" are
- * undefined. The error is never set if the function succeeds.
- *
- * @todo for thread safety, I think we have to use
- * readdir_r(). (GLib has the same issue, should file a bug.)
- *
- * @param iter the iterator
- * @param filename string to be set to the next file in the dir
- * @param error return location for error
- * @returns #TRUE if filename was filled in with a new filename
- */
-dbus_bool_t
-_dbus_directory_get_next_file (DBusDirIter *iter,
- DBusString *filename,
- DBusError *error)
-{
- struct dirent *ent;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-
- again:
- errno = 0;
- ent = readdir (iter->d);
- if (ent == NULL)
- {
- if (errno != 0)
- dbus_set_error (error,
- _dbus_error_from_errno (errno),
- "%s", _dbus_strerror (errno));
- return FALSE;
- }
- else if (ent->d_name[0] == '.' &&
- (ent->d_name[1] == '\0' ||
- (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
- goto again;
- else
- {
- _dbus_string_set_length (filename, 0);
- if (!_dbus_string_append (filename, ent->d_name))
- {
- dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
- "No memory to read directory entry");
- return FALSE;
- }
- else
- return TRUE;
- }
-}
-
-/**
- * Closes a directory iteration.
- */
-void
-_dbus_directory_close (DBusDirIter *iter)
-{
- closedir (iter->d);
- dbus_free (iter);
-}
-
-static dbus_bool_t
-fill_user_info_from_group (struct group *g,
- DBusGroupInfo *info,
- DBusError *error)
-{
- _dbus_assert (g->gr_name != NULL);
-
- info->gid = g->gr_gid;
- info->groupname = _dbus_strdup (g->gr_name);
-
- /* info->members = dbus_strdupv (g->gr_mem) */
-
- if (info->groupname == NULL)
- {
- dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static dbus_bool_t
-fill_group_info (DBusGroupInfo *info,
- dbus_gid_t gid,
- const DBusString *groupname,
- DBusError *error)
-{
- const char *group_c_str;
-
- _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
- _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
-
- if (groupname)
- group_c_str = _dbus_string_get_const_data (groupname);
- else
- group_c_str = NULL;
-
- /* For now assuming that the getgrnam() and getgrgid() flavors
- * always correspond to the pwnam flavors, if not we have
- * to add more configure checks.
- */
-
-#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
- {
- struct group *g;
- int result;
- char buf[1024];
- struct group g_str;
-
- g = NULL;
-#ifdef HAVE_POSIX_GETPWNAM_R
-
- if (group_c_str)
- result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
- &g);
- else
- result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
- &g);
-#else
- g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
- result = 0;
-#endif /* !HAVE_POSIX_GETPWNAM_R */
- if (result == 0 && g == &g_str)
- {
- return fill_user_info_from_group (g, info, error);
- }
- else
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Group %s unknown or failed to look it up\n",
- group_c_str ? group_c_str : "???");
- return FALSE;
- }
- }
-#else /* ! HAVE_GETPWNAM_R */
- {
- /* I guess we're screwed on thread safety here */
- struct group *g;
-
- g = getgrnam (group_c_str);
-
- if (g != NULL)
- {
- return fill_user_info_from_group (g, info, error);
- }
- else
- {
- dbus_set_error (error, _dbus_error_from_errno (errno),
- "Group %s unknown or failed to look it up\n",
- group_c_str ? group_c_str : "???");
- return FALSE;
- }
- }
-#endif /* ! HAVE_GETPWNAM_R */
-}
-
-/**
- * Initializes the given DBusGroupInfo struct
- * with information about the given group name.
- *
- * @param info the group info struct
- * @param groupname name of group
- * @param error the error return
- * @returns #FALSE if error is set
- */
-dbus_bool_t
-_dbus_group_info_fill (DBusGroupInfo *info,
- const DBusString *groupname,
- DBusError *error)
-{
- return fill_group_info (info, DBUS_GID_UNSET,
- groupname, error);
-
-}
-
-/**
- * Initializes the given DBusGroupInfo struct
- * with information about the given group ID.
- *
- * @param info the group info struct
- * @param gid group ID
- * @param error the error return
- * @returns #FALSE if error is set
- */
-dbus_bool_t
-_dbus_group_info_fill_gid (DBusGroupInfo *info,
- dbus_gid_t gid,
- DBusError *error)
-{
- return fill_group_info (info, gid, NULL, error);
-}
-
-/** @} */ /* End of DBusInternalsUtils functions */
-
-/**
- * @addtogroup DBusString
- *
- * @{
- */
-/**
- * Get the directory name from a complete filename
- * @param filename the filename
- * @param dirname string to append directory name to
- * @returns #FALSE if no memory
- */
-dbus_bool_t
-_dbus_string_get_dirname (const DBusString *filename,
- DBusString *dirname)
-{
- int sep;
-
- _dbus_assert (filename != dirname);
- _dbus_assert (filename != NULL);
- _dbus_assert (dirname != NULL);
-
- /* Ignore any separators on the end */
- sep = _dbus_string_get_length (filename);
- if (sep == 0)
- return _dbus_string_append (dirname, "."); /* empty string passed in */
-
- while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
- --sep;
-
- _dbus_assert (sep >= 0);
-
- if (sep == 0)
- return _dbus_string_append (dirname, "/");
-
- /* Now find the previous separator */
- _dbus_string_find_byte_backward (filename, sep, '/', &sep);
- if (sep < 0)
- return _dbus_string_append (dirname, ".");
-
- /* skip multiple separators */
- while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
- --sep;
-
- _dbus_assert (sep >= 0);
-
- if (sep == 0 &&
- _dbus_string_get_byte (filename, 0) == '/')
- return _dbus_string_append (dirname, "/");
- else
- return _dbus_string_copy_len (filename, 0, sep - 0,
- dirname, _dbus_string_get_length (dirname));
-}
-/** @} */ /* DBusString stuff */
-
-
#ifdef DBUS_BUILD_TESTS
#include <stdlib.h>
static void