1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-sysdeps-util.c Would be in dbus-sysdeps.c, but not used in libdbus
4 * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc.
5 * Copyright (C) 2003 CodeFactory AB
7 * Licensed under the Academic Free License version 2.1
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "dbus-sysdeps.h"
25 #include "dbus-internals.h"
26 #include "dbus-protocol.h"
27 #include "dbus-string.h"
28 #define DBUS_USERDB_INCLUDES_PRIVATE 1
29 #include "dbus-userdb.h"
30 #include "dbus-test.h"
32 #include <sys/types.h>
42 #include <sys/socket.h>
51 * @addtogroup DBusInternalsUtils
56 * Does the chdir, fork, setsid, etc. to become a daemon process.
58 * @param pidfile #NULL, or pidfile to create
59 * @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
60 * @param error return location for errors
61 * @returns #FALSE on failure
64 _dbus_become_daemon (const DBusString *pidfile,
72 _dbus_verbose ("Becoming a daemon...\n");
74 _dbus_verbose ("chdir to /\n");
77 dbus_set_error (error, DBUS_ERROR_FAILED,
78 "Could not chdir() to root directory");
82 _dbus_verbose ("forking...\n");
83 switch ((child_pid = fork ()))
86 _dbus_verbose ("fork failed\n");
87 dbus_set_error (error, _dbus_error_from_errno (errno),
88 "Failed to fork daemon: %s", _dbus_strerror (errno));
93 _dbus_verbose ("in child, closing std file descriptors\n");
95 /* silently ignore failures here, if someone
96 * doesn't have /dev/null we may as well try
100 dev_null_fd = open ("/dev/null", O_RDWR);
101 if (dev_null_fd >= 0)
103 dup2 (dev_null_fd, 0);
104 dup2 (dev_null_fd, 1);
106 s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
107 if (s == NULL || *s == '\0')
108 dup2 (dev_null_fd, 2);
110 _dbus_verbose ("keeping stderr open due to DBUS_DEBUG_OUTPUT\n");
113 /* Get a predictable umask */
114 _dbus_verbose ("setting umask\n");
121 _dbus_verbose ("parent writing pid file\n");
122 if (!_dbus_write_pid_file (pidfile,
126 _dbus_verbose ("pid file write failed, killing child\n");
127 kill (child_pid, SIGTERM);
132 /* Write PID if requested */
133 if (print_pid_fd >= 0)
138 if (!_dbus_string_init (&pid))
140 _DBUS_SET_OOM (error);
141 kill (child_pid, SIGTERM);
145 if (!_dbus_string_append_int (&pid, child_pid) ||
146 !_dbus_string_append (&pid, "\n"))
148 _dbus_string_free (&pid);
149 _DBUS_SET_OOM (error);
150 kill (child_pid, SIGTERM);
154 bytes = _dbus_string_get_length (&pid);
155 if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
157 dbus_set_error (error, DBUS_ERROR_FAILED,
158 "Printing message bus PID: %s\n",
159 _dbus_strerror (errno));
160 _dbus_string_free (&pid);
161 kill (child_pid, SIGTERM);
165 _dbus_string_free (&pid);
167 _dbus_verbose ("parent exiting\n");
172 _dbus_verbose ("calling setsid()\n");
174 _dbus_assert_not_reached ("setsid() failed");
181 * Creates a file containing the process ID.
183 * @param filename the filename to write to
184 * @param pid our process ID
185 * @param error return location for errors
186 * @returns #FALSE on failure
189 _dbus_write_pid_file (const DBusString *filename,
193 const char *cfilename;
197 cfilename = _dbus_string_get_const_data (filename);
199 fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
203 dbus_set_error (error, _dbus_error_from_errno (errno),
204 "Failed to open \"%s\": %s", cfilename,
205 _dbus_strerror (errno));
209 if ((f = fdopen (fd, "w")) == NULL)
211 dbus_set_error (error, _dbus_error_from_errno (errno),
212 "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
217 if (fprintf (f, "%lu\n", pid) < 0)
219 dbus_set_error (error, _dbus_error_from_errno (errno),
220 "Failed to write to \"%s\": %s", cfilename,
221 _dbus_strerror (errno));
225 if (fclose (f) == EOF)
227 dbus_set_error (error, _dbus_error_from_errno (errno),
228 "Failed to close \"%s\": %s", cfilename,
229 _dbus_strerror (errno));
238 * Changes the user and group the bus is running as.
240 * @param uid the new user ID
241 * @param gid the new group ID
242 * @param error return location for errors
243 * @returns #FALSE on failure
246 _dbus_change_identity (dbus_uid_t uid,
250 /* setgroups() only works if we are a privileged process,
251 * so we don't return error on failure; the only possible
252 * failure is that we don't have perms to do it.
253 * FIXME not sure this is right, maybe if setuid()
254 * is going to work then setgroups() should also work.
256 if (setgroups (0, NULL) < 0)
257 _dbus_warn ("Failed to drop supplementary groups: %s\n",
258 _dbus_strerror (errno));
260 /* Set GID first, or the setuid may remove our permission
263 if (setgid (gid) < 0)
265 dbus_set_error (error, _dbus_error_from_errno (errno),
266 "Failed to set GID to %lu: %s", gid,
267 _dbus_strerror (errno));
271 if (setuid (uid) < 0)
273 dbus_set_error (error, _dbus_error_from_errno (errno),
274 "Failed to set UID to %lu: %s", uid,
275 _dbus_strerror (errno));
282 /** Installs a UNIX signal handler
284 * @param sig the signal to handle
285 * @param handler the handler
288 _dbus_set_signal_handler (int sig,
289 DBusSignalHandler handler)
291 struct sigaction act;
294 sigemptyset (&empty_mask);
295 act.sa_handler = handler;
296 act.sa_mask = empty_mask;
298 sigaction (sig, &act, 0);
303 * Removes a directory; Directory must be empty
305 * @param filename directory filename
306 * @param error initialized error object
307 * @returns #TRUE on success
310 _dbus_delete_directory (const DBusString *filename,
313 const char *filename_c;
315 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
317 filename_c = _dbus_string_get_const_data (filename);
319 if (rmdir (filename_c) != 0)
321 dbus_set_error (error, DBUS_ERROR_FAILED,
322 "Failed to remove directory %s: %s\n",
323 filename_c, _dbus_strerror (errno));
330 /** Checks if a file exists
332 * @param file full path to the file
333 * @returns #TRUE if file exists
336 _dbus_file_exists (const char *file)
338 return (access (file, F_OK) == 0);
341 /** Checks if user is at the console
343 * @param username user to check
344 * @param error return location for errors
345 * @returns #TRUE is the user is at the consolei and there are no errors
348 _dbus_user_at_console (const char *username,
356 if (!_dbus_string_init (&f))
358 _DBUS_SET_OOM (error);
362 if (!_dbus_string_append (&f, DBUS_CONSOLE_AUTH_DIR))
364 _DBUS_SET_OOM (error);
369 if (!_dbus_string_append (&f, username))
371 _DBUS_SET_OOM (error);
375 result = _dbus_file_exists (_dbus_string_get_const_data (&f));
378 _dbus_string_free (&f);
385 * Checks whether the filename is an absolute path
387 * @param filename the filename
388 * @returns #TRUE if an absolute path
391 _dbus_path_is_absolute (const DBusString *filename)
393 if (_dbus_string_get_length (filename) > 0)
394 return _dbus_string_get_byte (filename, 0) == '/';
402 * @param filename the filename to stat
403 * @param statbuf the stat info to fill in
404 * @param error return location for error
405 * @returns #FALSE if error was set
408 _dbus_stat (const DBusString *filename,
412 const char *filename_c;
415 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
417 filename_c = _dbus_string_get_const_data (filename);
419 if (stat (filename_c, &sb) < 0)
421 dbus_set_error (error, _dbus_error_from_errno (errno),
422 "%s", _dbus_strerror (errno));
426 statbuf->mode = sb.st_mode;
427 statbuf->nlink = sb.st_nlink;
428 statbuf->uid = sb.st_uid;
429 statbuf->gid = sb.st_gid;
430 statbuf->size = sb.st_size;
431 statbuf->atime = sb.st_atime;
432 statbuf->mtime = sb.st_mtime;
433 statbuf->ctime = sb.st_ctime;
440 * Internals of directory iterator
444 DIR *d; /**< The DIR* from opendir() */
449 * Open a directory to iterate over.
451 * @param filename the directory name
452 * @param error exception return object or #NULL
453 * @returns new iterator, or #NULL on error
456 _dbus_directory_open (const DBusString *filename,
461 const char *filename_c;
463 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
465 filename_c = _dbus_string_get_const_data (filename);
467 d = opendir (filename_c);
470 dbus_set_error (error, _dbus_error_from_errno (errno),
471 "Failed to read directory \"%s\": %s",
473 _dbus_strerror (errno));
476 iter = dbus_new0 (DBusDirIter, 1);
480 dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
481 "Could not allocate memory for directory iterator");
491 * Get next file in the directory. Will not return "." or ".." on
492 * UNIX. If an error occurs, the contents of "filename" are
493 * undefined. The error is never set if the function succeeds.
495 * @todo for thread safety, I think we have to use
496 * readdir_r(). (GLib has the same issue, should file a bug.)
498 * @param iter the iterator
499 * @param filename string to be set to the next file in the dir
500 * @param error return location for error
501 * @returns #TRUE if filename was filled in with a new filename
504 _dbus_directory_get_next_file (DBusDirIter *iter,
505 DBusString *filename,
510 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
514 ent = readdir (iter->d);
518 dbus_set_error (error,
519 _dbus_error_from_errno (errno),
520 "%s", _dbus_strerror (errno));
523 else if (ent->d_name[0] == '.' &&
524 (ent->d_name[1] == '\0' ||
525 (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
529 _dbus_string_set_length (filename, 0);
530 if (!_dbus_string_append (filename, ent->d_name))
532 dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
533 "No memory to read directory entry");
542 * Closes a directory iteration.
545 _dbus_directory_close (DBusDirIter *iter)
552 fill_user_info_from_group (struct group *g,
556 _dbus_assert (g->gr_name != NULL);
558 info->gid = g->gr_gid;
559 info->groupname = _dbus_strdup (g->gr_name);
561 /* info->members = dbus_strdupv (g->gr_mem) */
563 if (info->groupname == NULL)
565 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
573 fill_group_info (DBusGroupInfo *info,
575 const DBusString *groupname,
578 const char *group_c_str;
580 _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
581 _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
584 group_c_str = _dbus_string_get_const_data (groupname);
588 /* For now assuming that the getgrnam() and getgrgid() flavors
589 * always correspond to the pwnam flavors, if not we have
590 * to add more configure checks.
593 #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
601 #ifdef HAVE_POSIX_GETPWNAM_R
604 result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
607 result = getgrgid_r (gid, &g_str, buf, sizeof (buf),
610 g = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
612 #endif /* !HAVE_POSIX_GETPWNAM_R */
613 if (result == 0 && g == &g_str)
615 return fill_user_info_from_group (g, info, error);
619 dbus_set_error (error, _dbus_error_from_errno (errno),
620 "Group %s unknown or failed to look it up\n",
621 group_c_str ? group_c_str : "???");
625 #else /* ! HAVE_GETPWNAM_R */
627 /* I guess we're screwed on thread safety here */
630 g = getgrnam (group_c_str);
634 return fill_user_info_from_group (g, info, error);
638 dbus_set_error (error, _dbus_error_from_errno (errno),
639 "Group %s unknown or failed to look it up\n",
640 group_c_str ? group_c_str : "???");
644 #endif /* ! HAVE_GETPWNAM_R */
648 * Initializes the given DBusGroupInfo struct
649 * with information about the given group name.
651 * @param info the group info struct
652 * @param groupname name of group
653 * @param error the error return
654 * @returns #FALSE if error is set
657 _dbus_group_info_fill (DBusGroupInfo *info,
658 const DBusString *groupname,
661 return fill_group_info (info, DBUS_GID_UNSET,
667 * Initializes the given DBusGroupInfo struct
668 * with information about the given group ID.
670 * @param info the group info struct
671 * @param gid group ID
672 * @param error the error return
673 * @returns #FALSE if error is set
676 _dbus_group_info_fill_gid (DBusGroupInfo *info,
680 return fill_group_info (info, gid, NULL, error);
684 * Frees the members of info (but not info itself).
686 * @param info the group info
689 _dbus_group_info_free (DBusGroupInfo *info)
691 dbus_free (info->groupname);
694 /** @} */ /* End of DBusInternalsUtils functions */
697 * @addtogroup DBusString
702 * Get the directory name from a complete filename
703 * @param filename the filename
704 * @param dirname string to append directory name to
705 * @returns #FALSE if no memory
708 _dbus_string_get_dirname (const DBusString *filename,
713 _dbus_assert (filename != dirname);
714 _dbus_assert (filename != NULL);
715 _dbus_assert (dirname != NULL);
717 /* Ignore any separators on the end */
718 sep = _dbus_string_get_length (filename);
720 return _dbus_string_append (dirname, "."); /* empty string passed in */
722 while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
725 _dbus_assert (sep >= 0);
728 return _dbus_string_append (dirname, "/");
730 /* Now find the previous separator */
731 _dbus_string_find_byte_backward (filename, sep, '/', &sep);
733 return _dbus_string_append (dirname, ".");
735 /* skip multiple separators */
736 while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
739 _dbus_assert (sep >= 0);
742 _dbus_string_get_byte (filename, 0) == '/')
743 return _dbus_string_append (dirname, "/");
745 return _dbus_string_copy_len (filename, 0, sep - 0,
746 dirname, _dbus_string_get_length (dirname));
748 /** @} */ /* DBusString stuff */
751 #ifdef DBUS_BUILD_TESTS
754 check_dirname (const char *filename,
759 _dbus_string_init_const (&f, filename);
761 if (!_dbus_string_init (&d))
762 _dbus_assert_not_reached ("no memory");
764 if (!_dbus_string_get_dirname (&f, &d))
765 _dbus_assert_not_reached ("no memory");
767 if (!_dbus_string_equal_c_str (&d, dirname))
769 _dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n",
771 _dbus_string_get_const_data (&d),
776 _dbus_string_free (&d);
780 check_path_absolute (const char *path,
781 dbus_bool_t expected)
785 _dbus_string_init_const (&p, path);
787 if (_dbus_path_is_absolute (&p) != expected)
789 _dbus_warn ("For path \"%s\" expected absolute = %d got %d\n",
790 path, expected, _dbus_path_is_absolute (&p));
796 * Unit test for dbus-sysdeps.c.
798 * @returns #TRUE on success.
801 _dbus_sysdeps_test (void)
807 check_dirname ("foo", ".");
808 check_dirname ("foo/bar", "foo");
809 check_dirname ("foo//bar", "foo");
810 check_dirname ("foo///bar", "foo");
811 check_dirname ("foo/bar/", "foo");
812 check_dirname ("foo//bar/", "foo");
813 check_dirname ("foo///bar/", "foo");
814 check_dirname ("foo/bar//", "foo");
815 check_dirname ("foo//bar////", "foo");
816 check_dirname ("foo///bar///////", "foo");
817 check_dirname ("/foo", "/");
818 check_dirname ("////foo", "/");
819 check_dirname ("/foo/bar", "/foo");
820 check_dirname ("/foo//bar", "/foo");
821 check_dirname ("/foo///bar", "/foo");
822 check_dirname ("/", "/");
823 check_dirname ("///", "/");
824 check_dirname ("", ".");
827 _dbus_string_init_const (&str, "3.5");
828 if (!_dbus_string_parse_double (&str,
831 _dbus_warn ("Failed to parse double");
834 if (ABS(3.5 - val) > 1e-6)
836 _dbus_warn ("Failed to parse 3.5 correctly, got: %f", val);
841 _dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos);
845 _dbus_string_init_const (&str, "0xff");
846 if (!_dbus_string_parse_double (&str,
849 _dbus_warn ("Failed to parse double");
852 if (ABS (0xff - val) > 1e-6)
854 _dbus_warn ("Failed to parse 0xff correctly, got: %f\n", val);
859 _dbus_warn ("_dbus_string_parse_double of \"0xff\" returned wrong position %d", pos);
863 check_path_absolute ("/", TRUE);
864 check_path_absolute ("/foo", TRUE);
865 check_path_absolute ("", FALSE);
866 check_path_absolute ("foo", FALSE);
867 check_path_absolute ("foo/bar", FALSE);
871 #endif /* DBUS_BUILD_TESTS */