From: Lennart Poettering Date: Tue, 25 May 2010 00:25:04 +0000 (+0200) Subject: systemd: add systemd-style socket-activation X-Git-Tag: dbus-1.4.0~28 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dd31130a971593b5e20a1c4cb04f93c2aae90c84;p=platform%2Fupstream%2Fdbus.git systemd: add systemd-style socket-activation This is a pretty straightforward implementation of systemd-style socket activation using the reference implementation of the $LISTEN_FDS/$LISTEN_PID env var parsing as supplied by systemd upstream. --- diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 906b4c7..f88c7b7 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -55,7 +55,7 @@ DBUS_LIB_arch_sources = \ if DBUS_WINCE wince_source = dbus-sysdeps-wince-glue.h dbus-sysdeps-wince-glue.c else -wince_source = +wince_source = endif DBUS_SHARED_arch_sources = \ @@ -88,7 +88,9 @@ DBUS_SHARED_arch_sources = \ dbus-transport-unix.c \ dbus-transport-unix.h \ dbus-userdb.c \ - dbus-userdb.h + dbus-userdb.h \ + sd-daemon.c \ + sd-daemon.h DBUS_UTIL_arch_sources = \ dbus-sysdeps-util-unix.c \ @@ -180,8 +182,8 @@ DBUS_LIB_SOURCES= \ ## dbus-md5.h \ ### source code that goes in the installed client library -### AND is generic utility functionality used by the -### daemon or test programs (all symbols in here should +### AND is generic utility functionality used by the +### daemon or test programs (all symbols in here should ### be underscore-prefixed) DBUS_SHARED_SOURCES= \ dbus-dataslot.c \ @@ -210,8 +212,8 @@ DBUS_SHARED_SOURCES= \ ### source code that is generic utility functionality used ### by the bus daemon or test apps, but is NOT included -### in the D-Bus client library (all symbols in here -### should be underscore-prefixed but don't really need +### in the D-Bus client library (all symbols in here +### should be underscore-prefixed but don't really need ### to be unless they move to DBUS_SHARED_SOURCES later) DBUS_UTIL_SOURCES= \ dbus-auth-util.c \ @@ -259,7 +261,7 @@ libdbus_internal_la_LIBADD=$(DBUS_CLIENT_LIBS) libdbus_internal_la_LDFLAGS=$(export_symbols_internal) @R_DYNAMIC_LDFLAG@ ## note that TESTS has special meaning (stuff to use in make check) -## so if adding tests not to be run in make check, don't add them to +## so if adding tests not to be run in make check, don't add them to ## TESTS if DBUS_BUILD_TESTS TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus @@ -268,7 +270,7 @@ else TESTS= endif -## we use noinst_PROGRAMS not check_PROGRAMS so that we build +## we use noinst_PROGRAMS not check_PROGRAMS so that we build ## even when not doing "make check" noinst_PROGRAMS=$(TESTS) @@ -281,3 +283,7 @@ dbus_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ ## mop up the gcov files clean-local: /bin/rm *.bb *.bbg *.da *.gcov .libs/*.da .libs/*.bbg || true + +update-systemd: + curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c > sd-daemon.c + curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h > sd-daemon.h diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c index 65c9668..3e5ad6c 100644 --- a/dbus/dbus-server-unix.c +++ b/dbus/dbus-server-unix.c @@ -4,7 +4,7 @@ * Copyright (C) 2002, 2003, 2004 Red Hat Inc. * * 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 @@ -14,7 +14,7 @@ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -42,12 +42,12 @@ * Tries to interpret the address entry in a platform-specific * way, creating a platform-specific server type if appropriate. * Sets error if the result is not OK. - * + * * @param entry an address entry * @param server_p location to store a new DBusServer, or #NULL on failure. * @param error location to store rationale for failure on bad address * @returns the outcome - * + * */ DBusServerListenResult _dbus_server_listen_platform_specific (DBusAddressEntry *entry, @@ -57,7 +57,7 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry, const char *method; *server_p = NULL; - + method = dbus_address_entry_get_method (entry); if (strcmp (method, "unix") == 0) @@ -65,7 +65,7 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry, const char *path = dbus_address_entry_get_value (entry, "path"); const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); const char *abstract = dbus_address_entry_get_value (entry, "abstract"); - + if (path == NULL && tmpdir == NULL && abstract == NULL) { _dbus_set_bad_address(error, "unix", @@ -87,20 +87,20 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry, { DBusString full_path; DBusString filename; - + if (!_dbus_string_init (&full_path)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; } - + if (!_dbus_string_init (&filename)) { _dbus_string_free (&full_path); dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; } - + if (!_dbus_string_append (&filename, "dbus-") || !_dbus_generate_random_ascii (&filename, 10) || @@ -112,9 +112,9 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry, dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; } - + /* Always use abstract namespace if possible with tmpdir */ - + *server_p = _dbus_server_new_for_domain_socket (_dbus_string_get_const_data (&full_path), #ifdef HAVE_ABSTRACT_SOCKETS @@ -146,6 +146,39 @@ _dbus_server_listen_platform_specific (DBusAddressEntry *entry, return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; } } + else if (strcmp (method, "systemd") == 0) + { + int n, *fds; + DBusString address; + + n = _dbus_listen_systemd_sockets (&fds, error); + if (n < 0) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + _dbus_string_init_const (&address, "systemd:"); + + *server_p = _dbus_server_new_for_socket (fds, n, &address, NULL); + if (*server_p == NULL) + { + int i; + + for (i = 0; i < n; i++) + { + _dbus_close_socket (fds[i], NULL); + } + dbus_free (fds); + + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; + } + + dbus_free (fds); + + return DBUS_SERVER_LISTEN_OK; + } else { /* If we don't handle the method, we return NULL with the @@ -174,7 +207,7 @@ _dbus_server_new_for_domain_socket (const char *path, DBusString address; char *path_copy; DBusString path_str; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (!_dbus_string_init (&address)) @@ -200,7 +233,7 @@ _dbus_server_new_for_domain_socket (const char *path, dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto failed_0; } - + listen_fd = _dbus_listen_unix_socket (path, abstract, error); if (listen_fd < 0) @@ -208,7 +241,7 @@ _dbus_server_new_for_domain_socket (const char *path, _DBUS_ASSERT_ERROR_IS_SET (error); goto failed_1; } - + server = _dbus_server_new_for_socket (&listen_fd, 1, &address, 0); if (server == NULL) { @@ -217,9 +250,9 @@ _dbus_server_new_for_domain_socket (const char *path, } _dbus_server_socket_own_filename(server, path_copy); - + _dbus_string_free (&address); - + return server; failed_2: @@ -233,4 +266,3 @@ _dbus_server_new_for_domain_socket (const char *path, } /** @} */ - diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index ebe9bdd..e859ec6 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1,11 +1,11 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation) - * + * * Copyright (C) 2002, 2003, 2006 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 @@ -15,7 +15,7 @@ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -76,6 +76,8 @@ #include #endif +#include "sd-daemon.h" + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -162,7 +164,7 @@ _dbus_open_unix_socket (int *fd, * @param error return location for an error * @returns #FALSE if error is set */ -dbus_bool_t +dbus_bool_t _dbus_close_socket (int fd, DBusError *error) { @@ -460,7 +462,7 @@ _dbus_write_socket_with_unix_fds_two(int fd, /** * Like _dbus_write_two() but only works on sockets and is thus * available on Windows. - * + * * @param fd the file descriptor * @param buffer1 first buffer * @param start1 first byte to write in first buffer @@ -543,7 +545,7 @@ _dbus_socket_is_invalid (int fd) * * Unlike _dbus_read_socket(), _dbus_read() is not available * on Windows. - * + * * @param fd the file descriptor to read from * @param buffer the buffer to append data to * @param count the amount of data to read @@ -559,7 +561,7 @@ _dbus_read (int fd, char *data; _dbus_assert (count >= 0); - + start = _dbus_string_get_length (buffer); if (!_dbus_string_lengthen (buffer, count)) @@ -571,7 +573,7 @@ _dbus_read (int fd, data = _dbus_string_get_data_len (buffer, start, count); again: - + bytes_read = read (fd, data, count); if (bytes_read < 0) @@ -594,7 +596,7 @@ _dbus_read (int fd, if (bytes_read > 0) _dbus_verbose_bytes_of_string (buffer, start, bytes_read); #endif - + return bytes_read; } } @@ -602,7 +604,7 @@ _dbus_read (int fd, /** * Thin wrapper around the write() system call that writes a part of a * DBusString and handles EINTR for you. - * + * * @param fd the file descriptor to write * @param buffer the buffer to write data from * @param start the first byte in the buffer to write @@ -617,9 +619,9 @@ _dbus_write (int fd, { const char *data; int bytes_written; - + data = _dbus_string_get_const_data_len (buffer, start, len); - + again: bytes_written = write (fd, data, len); @@ -631,7 +633,7 @@ _dbus_write (int fd, if (bytes_written > 0) _dbus_verbose_bytes_of_string (buffer, start, bytes_written); #endif - + return bytes_written; } @@ -669,7 +671,7 @@ _dbus_write_two (int fd, _dbus_assert (start2 >= 0); _dbus_assert (len1 >= 0); _dbus_assert (len2 >= 0); - + #ifdef HAVE_WRITEV { struct iovec vectors[2]; @@ -687,40 +689,40 @@ _dbus_write_two (int fd, start2 = 0; len2 = 0; } - + vectors[0].iov_base = (char*) data1; vectors[0].iov_len = len1; vectors[1].iov_base = (char*) data2; vectors[1].iov_len = len2; again: - + bytes_written = writev (fd, vectors, data2 ? 2 : 1); if (bytes_written < 0 && errno == EINTR) goto again; - + return bytes_written; } #else /* HAVE_WRITEV */ { int ret1; - + ret1 = _dbus_write (fd, buffer1, start1, len1); if (ret1 == len1 && buffer2 != NULL) { ret2 = _dbus_write (fd, buffer2, start2, len2); if (ret2 < 0) ret2 = 0; /* we can't report an error as the first write was OK */ - + return ret1 + ret2; } else return ret1; } -#endif /* !HAVE_WRITEV */ +#endif /* !HAVE_WRITEV */ } #define _DBUS_MAX_SUN_PATH_LENGTH 99 @@ -742,7 +744,7 @@ _dbus_write_two (int fd, * Creates a socket and connects it to the UNIX domain socket at the * given path. The connection fd is returned, and is set up as * nonblocking. - * + * * Uses abstract sockets instead of filesystem-linked sockets if * requested (it's possible only on Linux; see "man 7 unix" on Linux). * On non-Linux abstract socket usage always fails. @@ -761,14 +763,14 @@ _dbus_connect_unix_socket (const char *path, { int fd; size_t path_len; - struct sockaddr_un addr; + struct sockaddr_un addr; _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_verbose ("connecting to unix socket %s abstract=%d\n", path, abstract); - - + + if (!_dbus_open_unix_socket (&fd, error)) { _DBUS_ASSERT_ERROR_IS_SET(error); @@ -793,7 +795,7 @@ _dbus_connect_unix_socket (const char *path, _dbus_close (fd, NULL); return -1; } - + strncpy (&addr.sun_path[1], path, path_len); /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ #else /* HAVE_ABSTRACT_SOCKETS */ @@ -815,9 +817,9 @@ _dbus_connect_unix_socket (const char *path, strncpy (addr.sun_path, path, path_len); } - + if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) - { + { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to connect to socket %s: %s", @@ -825,14 +827,14 @@ _dbus_connect_unix_socket (const char *path, _dbus_close (fd, NULL); fd = -1; - + return -1; } if (!_dbus_set_fd_nonblocking (fd, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); - + _dbus_close (fd, NULL); fd = -1; @@ -857,10 +859,10 @@ _dbus_set_local_creds (int fd, dbus_bool_t on) dbus_bool_t retval = TRUE; #if defined(HAVE_CMSGCRED) - /* NOOP just to make sure only one codepath is used + /* NOOP just to make sure only one codepath is used * and to prefer CMSGCRED */ -#elif defined(LOCAL_CREDS) +#elif defined(LOCAL_CREDS) int val = on ? 1 : 0; if (setsockopt (fd, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) { @@ -905,7 +907,7 @@ _dbus_listen_unix_socket (const char *path, _dbus_verbose ("listening on unix socket %s abstract=%d\n", path, abstract); - + if (!_dbus_open_unix_socket (&listen_fd, error)) { _DBUS_ASSERT_ERROR_IS_SET(error); @@ -916,7 +918,7 @@ _dbus_listen_unix_socket (const char *path, _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; path_len = strlen (path); - + if (abstract) { #ifdef HAVE_ABSTRACT_SOCKETS @@ -933,7 +935,7 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + strncpy (&addr.sun_path[1], path, path_len); /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ #else /* HAVE_ABSTRACT_SOCKETS */ @@ -970,10 +972,10 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + strncpy (addr.sun_path, path, path_len); } - + if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), @@ -1007,19 +1009,118 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + /* Try opening up the permissions, but if we can't, just go ahead * and continue, maybe it will be good enough. */ if (!abstract && chmod (path, 0777) < 0) _dbus_warn ("Could not set mode 0777 on socket %s\n", path); - + return listen_fd; } /** - * Creates a socket and connects to a socket at the given host + * Acquires one or more sockets passed in from systemd. The sockets + * are set to be nonblocking. + * + * This will set FD_CLOEXEC for the sockets returned. + * + * @oaram fds the file descriptors + * @param error return location for errors + * @returns the number of file descriptors + */ +int +_dbus_listen_systemd_sockets (int **fds, + DBusError *error) +{ + int r, n; + unsigned fd; + int *new_fds; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + n = sd_listen_fds (TRUE); + if (n < 0) + { + dbus_set_error (error, _dbus_error_from_errno (-n), + "Failed to acquire systemd socket: %s", + _dbus_strerror (-n)); + return -1; + } + + if (n <= 0) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "No socket received."); + return -1; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + r = sd_is_socket (fd, AF_UNSPEC, SOCK_STREAM, 1); + if (r < 0) + { + dbus_set_error (error, _dbus_error_from_errno (-r), + "Failed to verify systemd socket type: %s", + _dbus_strerror (-r)); + return -1; + } + + if (!r) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Passed socket has wrong type."); + return -1; + } + } + + /* OK, the file descriptors are all good, so let's take posession of + them then. */ + + new_fds = dbus_new (int, n); + if (!new_fds) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Failed to allocate file handle array."); + goto fail; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + if (!_dbus_set_local_creds (fd, TRUE)) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to enable LOCAL_CREDS on systemd socket: %s", + _dbus_strerror (errno)); + goto fail; + } + + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto fail; + } + + new_fds[fd - SD_LISTEN_FDS_START] = fd; + } + + *fds = new_fds; + return n; + + fail: + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + _dbus_close (fd, NULL); + } + + dbus_free (new_fds); + return -1; +} + +/** + * Creates a socket and connects to a socket at the given host * and port. The connection fd is returned, and is set up as * nonblocking. * @@ -1349,7 +1450,7 @@ write_credentials_byte (int server_fd, { int bytes_written; char buf[1] = { '\0' }; -#if defined(HAVE_CMSGCRED) +#if defined(HAVE_CMSGCRED) union { struct cmsghdr hdr; char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; @@ -1372,10 +1473,10 @@ write_credentials_byte (int server_fd, #endif _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + again: -#if defined(HAVE_CMSGCRED) +#if defined(HAVE_CMSGCRED) bytes_written = sendmsg (server_fd, &msg, 0); #else bytes_written = write (server_fd, buf, 1); @@ -1415,7 +1516,7 @@ write_credentials_byte (int server_fd, * we got valid credentials. On some systems, such as Linux, * reading/writing the byte isn't actually required, but we do it * anyway just to avoid multiple codepaths. - * + * * Fails if no byte is available, so you must select() first. * * The point of the byte is that on some systems we have to @@ -1437,8 +1538,8 @@ _dbus_read_credentials_socket (int client_fd, dbus_uid_t uid_read; dbus_pid_t pid_read; int bytes_read; - -#ifdef HAVE_CMSGCRED + +#ifdef HAVE_CMSGCRED union { struct cmsghdr hdr; char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; @@ -1455,7 +1556,7 @@ _dbus_read_credentials_socket (int client_fd, pid_read = DBUS_PID_UNSET; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + /* The POSIX spec certainly doesn't promise this, but * we need these assertions to fail as soon as we're wrong about * it so we can do the porting fixups @@ -1497,7 +1598,7 @@ _dbus_read_credentials_socket (int client_fd, * normally only call read_credentials if the socket was ready * for reading */ - + dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to read credentials byte: %s", _dbus_strerror (errno)); @@ -1533,9 +1634,9 @@ _dbus_read_credentials_socket (int client_fd, { #ifdef SO_PEERCRED - struct ucred cr; + struct ucred cr; int cr_len = sizeof (cr); - + if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && cr_len == sizeof (cr)) { @@ -1585,9 +1686,9 @@ _dbus_read_credentials_socket (int client_fd, { _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno)); } - else + else { - if (adt_set_from_ucred (adth, ucred, ADT_NEW)) + if (adt_set_from_ucred (adth, ucred, ADT_NEW)) { _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno)); } @@ -1643,7 +1744,7 @@ _dbus_read_credentials_socket (int client_fd, return FALSE; } } - + return TRUE; } @@ -1669,7 +1770,7 @@ _dbus_send_credentials_socket (int server_fd, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + if (write_credentials_byte (server_fd, error)) return TRUE; else @@ -1729,8 +1830,8 @@ _dbus_accept (int listen_fd) } /** - * Checks to make sure the given directory is - * private to the user + * Checks to make sure the given directory is + * private to the user * * @param dir the name of the directory * @param error error return @@ -1741,19 +1842,19 @@ _dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) { const char *directory; struct stat sb; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + directory = _dbus_string_get_const_data (dir); - + if (stat (directory, &sb) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "%s", _dbus_strerror (errno)); - + return FALSE; } - + if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) || (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode)) { @@ -1761,7 +1862,7 @@ _dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) "%s directory is not private to the user", directory); return FALSE; } - + return TRUE; } @@ -1772,12 +1873,12 @@ fill_user_info_from_passwd (struct passwd *p, { _dbus_assert (p->pw_name != NULL); _dbus_assert (p->pw_dir != NULL); - + info->uid = p->pw_uid; info->primary_gid = p->pw_gid; info->username = _dbus_strdup (p->pw_name); info->homedir = _dbus_strdup (p->pw_dir); - + if (info->username == NULL || info->homedir == NULL) { @@ -1795,7 +1896,7 @@ fill_user_info (DBusUserInfo *info, DBusError *error) { const char *username_c; - + /* exactly one of username/uid provided */ _dbus_assert (username != NULL || uid != DBUS_UID_UNSET); _dbus_assert (username == NULL || uid == DBUS_UID_UNSET); @@ -1806,7 +1907,7 @@ fill_user_info (DBusUserInfo *info, info->n_group_ids = 0; info->username = NULL; info->homedir = NULL; - + if (username != NULL) username_c = _dbus_string_get_const_data (username); else @@ -1816,7 +1917,7 @@ fill_user_info (DBusUserInfo *info, * are always symmetrical, if not we have to add more configure * checks */ - + #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R) { struct passwd *p; @@ -1920,7 +2021,7 @@ fill_user_info (DBusUserInfo *info, /* Fill this in so we can use it to get groups */ username_c = info->username; - + #ifdef HAVE_GETGROUPLIST { gid_t *buf; @@ -1936,7 +2037,7 @@ fill_user_info (DBusUserInfo *info, dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto failed; } - + if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0) @@ -1953,10 +2054,10 @@ fill_user_info (DBusUserInfo *info, for the "id" command, and it turns out that they use an undocumented library function getgrouplist_2 (!) which is not declared in any header in /usr/include (!!). That did not seem - like the way to go here. + like the way to go here. */ - if (buf_count == initial_buf_count) - { + if (buf_count == initial_buf_count) + { buf_count *= 16; /* Retry with an arbitrarily scaled-up array */ } new = dbus_realloc (buf, buf_count * sizeof (buf[0])); @@ -1966,7 +2067,7 @@ fill_user_info (DBusUserInfo *info, dbus_free (buf); goto failed; } - + buf = new; errno = 0; @@ -1976,7 +2077,7 @@ fill_user_info (DBusUserInfo *info, { _dbus_warn ("It appears that username \"%s\" is in more than %d groups.\nProceeding with just the first %d groups.", username_c, buf_count, buf_count); - } + } else { dbus_set_error (error, @@ -1998,12 +2099,12 @@ fill_user_info (DBusUserInfo *info, dbus_free (buf); goto failed; } - + for (i = 0; i < buf_count; ++i) info->group_ids[i] = buf[i]; info->n_group_ids = buf_count; - + dbus_free (buf); } #else /* HAVE_GETGROUPLIST */ @@ -2023,9 +2124,9 @@ fill_user_info (DBusUserInfo *info, #endif /* HAVE_GETGROUPLIST */ _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + return TRUE; - + failed: _DBUS_ASSERT_ERROR_IS_SET (error); return FALSE; @@ -2098,7 +2199,7 @@ _dbus_credentials_add_from_current_process (DBusCredentials *credentials) * is required, that is done in dbus-auth.c. The username here * need not be anything human-readable, it can be the machine-readable * form i.e. a user id. - * + * * @param str the string to append to * @returns #FALSE on no memory */ @@ -2140,7 +2241,7 @@ _dbus_geteuid (void) /** * The only reason this is separate from _dbus_getpid() is to allow it * on Windows for logging but not for other purposes. - * + * * @returns process ID to put in log messages */ unsigned long @@ -2162,7 +2263,7 @@ _dbus_parse_uid (const DBusString *uid_str, { int end; long val; - + if (_dbus_string_get_length (uid_str) == 0) { _dbus_verbose ("UID string was zero length\n"); @@ -2177,7 +2278,7 @@ _dbus_parse_uid (const DBusString *uid_str, _dbus_verbose ("could not parse string as a UID\n"); return FALSE; } - + if (end != _dbus_string_get_length (uid_str)) { _dbus_verbose ("string contained trailing stuff after UID\n"); @@ -2227,7 +2328,7 @@ _dbus_atomic_dec (DBusAtomic *atomic) return __sync_sub_and_fetch(&atomic->value, 1)+1; #else dbus_int32_t res; - + _DBUS_LOCK (atomic); res = atomic->value; atomic->value -= 1; @@ -2280,7 +2381,7 @@ _dbus_poll (DBusPollFD *fds, _DBUS_STRUCT_OFFSET (struct pollfd, revents)) { return poll ((struct pollfd*) fds, - n_fds, + n_fds, timeout_milliseconds); } else @@ -2298,7 +2399,7 @@ _dbus_poll (DBusPollFD *fds, int i; struct timeval tv; int ready; - + FD_ZERO (&read_set); FD_ZERO (&write_set); FD_ZERO (&err_set); @@ -2317,7 +2418,7 @@ _dbus_poll (DBusPollFD *fds, max_fd = MAX (max_fd, fdp->fd); } - + tv.tv_sec = timeout_milliseconds / 1000; tv.tv_usec = (timeout_milliseconds % 1000) * 1000; @@ -2393,14 +2494,14 @@ _dbus_create_directory (const DBusString *filename, const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + filename_c = _dbus_string_get_const_data (filename); if (mkdir (filename_c, 0700) < 0) { if (errno == EEXIST) return TRUE; - + dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to create directory %s: %s\n", filename_c, _dbus_strerror (errno)); @@ -2430,7 +2531,7 @@ _dbus_concat_dir_and_file (DBusString *dir, if (_dbus_string_get_length (dir) == 0 || _dbus_string_get_length (next_component) == 0) return TRUE; - + dir_ends_in_slash = '/' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1); @@ -2492,7 +2593,7 @@ _dbus_generate_pseudorandom_bytes (DBusString *str, { int old_len; char *p; - + old_len = _dbus_string_get_length (str); if (!_dbus_string_lengthen (str, n_bytes)) @@ -2525,7 +2626,7 @@ _dbus_generate_random_bytes (DBusString *str, * a DBusError. So we always fall back to pseudorandom * if the I/O fails. */ - + old_len = _dbus_string_get_length (str); fd = -1; @@ -2535,7 +2636,7 @@ _dbus_generate_random_bytes (DBusString *str, return _dbus_generate_pseudorandom_bytes (str, n_bytes); _dbus_verbose ("/dev/urandom fd %d opened\n", fd); - + if (_dbus_read (fd, str, n_bytes) != n_bytes) { _dbus_close (fd, NULL); @@ -2545,9 +2646,9 @@ _dbus_generate_random_bytes (DBusString *str, _dbus_verbose ("Read %d bytes from /dev/urandom\n", n_bytes); - + _dbus_close (fd, NULL); - + return TRUE; } @@ -2574,7 +2675,7 @@ const char* _dbus_strerror (int error_number) { const char *msg; - + msg = strerror (error_number); if (msg == NULL) msg = "unknown"; @@ -2602,14 +2703,14 @@ void _dbus_fd_set_close_on_exec (intptr_t fd) { int val; - + val = fcntl (fd, F_GETFD, 0); - + if (val < 0) return; val |= FD_CLOEXEC; - + fcntl (fd, F_SETFD, val); } @@ -2625,7 +2726,7 @@ _dbus_close (int fd, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + again: if (close (fd) < 0) { @@ -2696,7 +2797,7 @@ _dbus_set_fd_nonblocking (int fd, int val; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + val = fcntl (fd, F_GETFL, 0); if (val < 0) { @@ -2729,17 +2830,17 @@ _dbus_set_fd_nonblocking (int fd, */ void _dbus_print_backtrace (void) -{ +{ #if defined (HAVE_BACKTRACE) && defined (DBUS_BUILT_R_DYNAMIC) void *bt[500]; int bt_size; int i; char **syms; - + bt_size = backtrace (bt, 500); syms = backtrace_symbols (bt, bt_size); - + i = 0; while (i < bt_size) { @@ -2767,7 +2868,7 @@ _dbus_print_backtrace (void) * principle it could be in dbus-sysdeps-util.c, except that * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the * debug-pipe server is used. - * + * * @param fd1 return location for one end * @param fd2 return location for the other end * @param blocking #TRUE if pipe should be blocking @@ -2819,20 +2920,20 @@ _dbus_full_duplex_pipe (int *fd1, { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not set full-duplex pipe nonblocking"); - + _dbus_close (fds[0], NULL); _dbus_close (fds[1], NULL); - + return FALSE; } - + *fd1 = fds[0]; *fd2 = fds[1]; _dbus_verbose ("full-duplex pipe %d <-> %d\n", *fd1, *fd2); - - return TRUE; + + return TRUE; #else _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n"); dbus_set_error (error, DBUS_ERROR_FAILED, @@ -2858,7 +2959,7 @@ _dbus_printf_string_upper_bound (const char *format, } /** - * Gets the temporary files directory by inspecting the environment variables + * Gets the temporary files directory by inspecting the environment variables * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned * * @returns location of temp directory @@ -2889,9 +2990,9 @@ _dbus_get_tmpdir(void) if (tmpdir == NULL) tmpdir = "/tmp"; } - + _dbus_assert(tmpdir != NULL); - + return tmpdir; } @@ -2931,7 +3032,7 @@ _read_subprocess_line_argv (const char *progpath, dbus_bool_t retval; sigset_t new_set, old_set; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); retval = FALSE; @@ -2942,9 +3043,9 @@ _read_subprocess_line_argv (const char *progpath, sigemptyset (&new_set); sigaddset (&new_set, SIGCHLD); sigprocmask (SIG_BLOCK, &new_set, &old_set); - + orig_len = _dbus_string_get_length (result); - + #define READ_END 0 #define WRITE_END 1 if (pipe (result_pipe) < 0) @@ -2989,7 +3090,7 @@ _read_subprocess_line_argv (const char *progpath, _exit (1); _dbus_verbose ("/dev/null fd %d opened\n", fd); - + /* set-up stdXXX */ close (result_pipe[READ_END]); close (errors_pipe[READ_END]); @@ -3044,7 +3145,7 @@ _read_subprocess_line_argv (const char *progpath, errors_pipe[WRITE_END] = -1; ret = 0; - do + do { ret = _dbus_read (result_pipe[READ_END], result, 1024); } @@ -3084,7 +3185,7 @@ _read_subprocess_line_argv (const char *progpath, } retval = TRUE; - + out: sigprocmask (SIG_SETMASK, &old_set, NULL); @@ -3102,7 +3203,7 @@ _read_subprocess_line_argv (const char *progpath, if (errors_pipe[1] != -1) close (errors_pipe[1]); - return retval; + return retval; } /** @@ -3124,7 +3225,7 @@ _dbus_get_autolaunch_address (DBusString *address, int i; DBusString uuid; dbus_bool_t retval; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); retval = FALSE; @@ -3133,13 +3234,13 @@ _dbus_get_autolaunch_address (DBusString *address, _DBUS_SET_OOM (error); return FALSE; } - + if (!_dbus_get_local_machine_uuid_encoded (&uuid)) { _DBUS_SET_OOM (error); goto out; } - + i = 0; argv[i] = "dbus-launch"; ++i; @@ -3198,14 +3299,14 @@ _dbus_read_local_machine_uuid (DBusGUID *machine_id, /** * Determines the address of the session bus by querying a - * platform-specific method. + * platform-specific method. * * The first parameter will be a boolean specifying whether * or not a dynamic session lookup is supported on this platform. - * + * * If supported is TRUE and the return value is #TRUE, the * address will be appended to @p address. - * If a failure happens, returns #FALSE and sets an error in + * If a failure happens, returns #FALSE and sets an error in * @p error. * * If supported is FALSE, ignore the return value. @@ -3222,15 +3323,15 @@ _dbus_lookup_session_address (dbus_bool_t *supported, { /* On non-Mac Unix platforms, if the session address isn't already * set in DBUS_SESSION_BUS_ADDRESS environment variable, we punt and - * fall back to the autolaunch: global default; see + * fall back to the autolaunch: global default; see * init_session_address in dbus/dbus-bus.c. */ *supported = FALSE; return TRUE; } /** - * Returns the standard directories for a session bus to look for service - * activation files + * Returns the standard directories for a session bus to look for service + * activation files * * On UNIX this should be the standard xdg freedesktop.org data directories: * @@ -3242,10 +3343,10 @@ _dbus_lookup_session_address (dbus_bool_t *supported, * DBUS_DATADIR * * @param dirs the directory list we are returning - * @returns #FALSE on OOM + * @returns #FALSE on OOM */ -dbus_bool_t +dbus_bool_t _dbus_get_standard_session_servicedirs (DBusList **dirs) { const char *xdg_data_home; @@ -3272,11 +3373,11 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) goto oom; } - /* + /* * add configured datadir to defaults * this may be the same as an xdg dir - * however the config parser should take - * care of duplicates + * however the config parser should take + * care of duplicates */ if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) goto oom; @@ -3293,7 +3394,7 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) if (!_dbus_homedir_from_current_process (&homedir)) goto oom; - + if (!_dbus_string_append (&servicedir_path, _dbus_string_get_const_data (homedir))) goto oom; @@ -3302,12 +3403,12 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) goto oom; } - if (!_dbus_split_paths_and_append (&servicedir_path, - DBUS_UNIX_STANDARD_SESSION_SERVICEDIR, + if (!_dbus_split_paths_and_append (&servicedir_path, + DBUS_UNIX_STANDARD_SESSION_SERVICEDIR, dirs)) goto oom; - _dbus_string_free (&servicedir_path); + _dbus_string_free (&servicedir_path); return TRUE; oom: @@ -3317,8 +3418,8 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) /** - * Returns the standard directories for a system bus to look for service - * activation files + * Returns the standard directories for a system bus to look for service + * activation files * * On UNIX this should be the standard xdg freedesktop.org data directories: * @@ -3331,10 +3432,10 @@ _dbus_get_standard_session_servicedirs (DBusList **dirs) * On Windows there is no system bus and this function can return nothing. * * @param dirs the directory list we are returning - * @returns #FALSE on OOM + * @returns #FALSE on OOM */ -dbus_bool_t +dbus_bool_t _dbus_get_standard_system_servicedirs (DBusList **dirs) { const char *xdg_data_dirs; @@ -3359,21 +3460,21 @@ _dbus_get_standard_system_servicedirs (DBusList **dirs) goto oom; } - /* + /* * add configured datadir to defaults * this may be the same as an xdg dir - * however the config parser should take - * care of duplicates + * however the config parser should take + * care of duplicates */ if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) goto oom; - if (!_dbus_split_paths_and_append (&servicedir_path, - DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, + if (!_dbus_split_paths_and_append (&servicedir_path, + DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, dirs)) goto oom; - _dbus_string_free (&servicedir_path); + _dbus_string_free (&servicedir_path); return TRUE; oom: @@ -3385,7 +3486,7 @@ _dbus_get_standard_system_servicedirs (DBusList **dirs) * Append the absolute path of the system.conf file * (there is no system bus on Windows so this can just * return FALSE and print a warning or something) - * + * * @param str the string to append to * @returns #FALSE if no memory */ @@ -3397,7 +3498,7 @@ _dbus_append_system_config_file (DBusString *str) /** * Append the absolute path of the session.conf file. - * + * * @param str the string to append to * @returns #FALSE if no memory */ @@ -3412,7 +3513,7 @@ _dbus_append_session_config_file (DBusString *str) * caches should be nuked. Of course any caches that need explicit reload * are probably broken, but c'est la vie. * - * + * */ void _dbus_flush_caches (void) @@ -3427,10 +3528,10 @@ _dbus_flush_caches (void) * * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably * be something else, since the dotfile convention is not normal on Windows. - * + * * @param directory string to append directory to * @param credentials credentials the directory should be for - * + * * @returns #FALSE on no memory */ dbus_bool_t @@ -3440,10 +3541,10 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, DBusString homedir; DBusString dotdir; dbus_uid_t uid; - + _dbus_assert (credentials != NULL); _dbus_assert (!_dbus_credentials_are_anonymous (credentials)); - + if (!_dbus_string_init (&homedir)) return FALSE; @@ -3452,11 +3553,11 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, if (!_dbus_homedir_from_uid (uid, &homedir)) goto failed; - + #ifdef DBUS_BUILD_TESTS { const char *override; - + override = _dbus_getenv ("DBUS_TEST_HOMEDIR"); if (override != NULL && *override != '\0') { @@ -3483,7 +3584,7 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, if (!_dbus_concat_dir_and_file (&homedir, &dotdir)) goto failed; - + if (!_dbus_string_copy (&homedir, 0, directory, _dbus_string_get_length (directory))) { goto failed; @@ -3491,8 +3592,8 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, _dbus_string_free (&homedir); return TRUE; - - failed: + + failed: _dbus_string_free (&homedir); return FALSE; } diff --git a/dbus/dbus-sysdeps-unix.h b/dbus/dbus-sysdeps-unix.h index 81758e0..807d2cf 100644 --- a/dbus/dbus-sysdeps-unix.h +++ b/dbus/dbus-sysdeps-unix.h @@ -1,11 +1,11 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-sysdeps-unix.h UNIX-specific wrappers around system/libc features (internal to D-Bus implementation) - * + * * Copyright (C) 2002, 2003, 2006 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 @@ -15,7 +15,7 @@ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -40,7 +40,7 @@ DBUS_BEGIN_DECLS * @{ */ -dbus_bool_t +dbus_bool_t _dbus_close (int fd, DBusError *error); int _dbus_dup (int fd, @@ -49,12 +49,12 @@ int _dbus_read (int fd, DBusString *buffer, int count); -int +int _dbus_write (int fd, const DBusString *buffer, int start, int len); -int +int _dbus_write_two (int fd, const DBusString *buffer1, int start1, @@ -72,6 +72,9 @@ int _dbus_listen_unix_socket (const char *path, dbus_bool_t abstract, DBusError *error); +int _dbus_listen_systemd_sockets (int **fd, + DBusError *error); + dbus_bool_t _dbus_read_credentials (int client_fd, DBusCredentials *credentials, DBusError *error); diff --git a/dbus/sd-daemon.c b/dbus/sd-daemon.c new file mode 100644 index 0000000..5df70e3 --- /dev/null +++ b/dbus/sd-daemon.c @@ -0,0 +1,448 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + Copyright 2010 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sd-daemon.h" + +int sd_listen_fds(int unset_environment) { + +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + int r, fd; + const char *e; + char *p = NULL; + unsigned long l; + + if (!(e = getenv("LISTEN_PID"))) { + r = 0; + goto finish; + } + + errno = 0; + l = strtoul(e, &p, 10); + + if (errno != 0) { + r = -errno; + goto finish; + } + + if (!p || *p || l <= 0) { + r = -EINVAL; + goto finish; + } + + /* Is this for us? */ + if (getpid() != (pid_t) l) { + r = 0; + goto finish; + } + + if (!(e = getenv("LISTEN_FDS"))) { + r = 0; + goto finish; + } + + errno = 0; + l = strtoul(e, &p, 10); + + if (errno != 0) { + r = -errno; + goto finish; + } + + if (!p || *p) { + r = -EINVAL; + goto finish; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { + int flags; + + if ((flags = fcntl(fd, F_GETFD)) < 0) { + r = -errno; + goto finish; + } + + if (flags & FD_CLOEXEC) + continue; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + r = -errno; + goto finish; + } + } + + r = (int) l; + +finish: + if (unset_environment) { + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + } + + return r; +#endif +} + +int sd_is_fifo(int fd, const char *path) { + struct stat st_fd; + + if (fd < 0) + return -EINVAL; + + memset(&st_fd, 0, sizeof(st_fd)); + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISFIFO(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + memset(&st_path, 0, sizeof(st_path)); + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + } + + return 1; +} + +static int sd_is_socket_internal(int fd, int type, int listening) { + struct stat st_fd; + + if (fd < 0 || type < 0) + return -EINVAL; + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISSOCK(st_fd.st_mode)) + return 0; + + if (type != 0) { + int other_type = 0; + socklen_t l = sizeof(other_type); + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) + return -errno; + + if (l != sizeof(other_type)) + return -EINVAL; + + if (other_type != type) + return 0; + } + + if (listening >= 0) { + int accepting = 0; + socklen_t l = sizeof(accepting); + + if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) + return -errno; + + if (l != sizeof(accepting)) + return -EINVAL; + + if (!accepting != !listening) + return 0; + } + + return 1; +} + +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; +}; + +int sd_is_socket(int fd, int family, int type, int listening) { + int r; + + if (family < 0) + return -EINVAL; + + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + return r; + + if (family > 0) { + union sockaddr_union sockaddr; + socklen_t l; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + return sockaddr.sa.sa_family == family; + } + + return 1; +} + +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { + union sockaddr_union sockaddr; + socklen_t l; + int r; + + if (family != 0 && family != AF_INET && family != AF_INET6) + return -EINVAL; + + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + return r; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_INET && + sockaddr.sa.sa_family != AF_INET6) + return 0; + + if (family > 0) + if (sockaddr.sa.sa_family != family) + return 0; + + if (port > 0) { + if (sockaddr.sa.sa_family == AF_INET) { + if (l < sizeof(struct sockaddr_in)) + return -EINVAL; + + return htons(port) == sockaddr.in4.sin_port; + } else { + if (l < sizeof(struct sockaddr_in6)) + return -EINVAL; + + return htons(port) == sockaddr.in6.sin6_port; + } + } + + return 1; +} + +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { + union sockaddr_union sockaddr; + socklen_t l; + int r; + + if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + return r; + + memset(&sockaddr, 0, sizeof(sockaddr)); + l = sizeof(sockaddr); + + if (getsockname(fd, &sockaddr.sa, &l) < 0) + return -errno; + + if (l < sizeof(sa_family_t)) + return -EINVAL; + + if (sockaddr.sa.sa_family != AF_UNIX) + return 0; + + if (path) { + if (length <= 0) + length = strlen(path); + + if (length <= 0) + /* Unnamed socket */ + return l == sizeof(sa_family_t); + + if (path[0]) + /* Normal path socket */ + return + (l >= sizeof(sa_family_t) + length + 1) && + memcmp(path, sockaddr.un.sun_path, length+1) == 0; + else + /* Abstract namespace socket */ + return + (l == sizeof(sa_family_t) + length) && + memcmp(path, sockaddr.un.sun_path, length) == 0; + } + + return 1; +} + +int sd_notify(int unset_environment, const char *state) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + int fd = -1, r; + struct msghdr msghdr; + struct iovec iovec; + union sockaddr_union sockaddr; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + const char *e; + + if (!state) { + r = -EINVAL; + goto finish; + } + + if (!(e = getenv("NOTIFY_SOCKET"))) + return 0; + + /* Must be an abstract socket, or an absolute path */ + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + r = -EINVAL; + goto finish; + } + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + goto finish; + } + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sa.sa_family = AF_UNIX; + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); + + if (sockaddr.un.sun_path[0] == '@') + sockaddr.un.sun_path[0] = 0; + + memset(&iovec, 0, sizeof(iovec)); + iovec.iov_base = (char*) state; + iovec.iov_len = strlen(state); + + memset(&control, 0, sizeof(control)); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = &sockaddr; + msghdr.msg_namelen = sizeof(struct sockaddr_un); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { + r = -errno; + goto finish; + } + + r = 1; + +finish: + if (unset_environment) + unsetenv("NOTIFY_SOCKET"); + + if (fd >= 0) + close(fd); + + return r; +#endif +} + +int sd_notifyf(int unset_environment, const char *format, ...) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + va_list ap; + char *p = NULL; + int r; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + + r = sd_notify(unset_environment, p); + free(p); + + return r; +#endif +} + +int sd_booted(void) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + + struct stat a, b; + + /* We simply test whether the systemd cgroup hierarchy is + * mounted */ + + if (lstat("/cgroup", &a) < 0) + return 0; + + if (lstat("/cgroup/systemd", &b) < 0) + return 0; + + return a.st_dev != b.st_dev; +#endif +} diff --git a/dbus/sd-daemon.h b/dbus/sd-daemon.h new file mode 100644 index 0000000..fd6221f --- /dev/null +++ b/dbus/sd-daemon.h @@ -0,0 +1,257 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foosddaemonhfoo +#define foosddaemonhfoo + +/*** + Copyright 2010 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Reference implementation of a few systemd related interfaces for + writing daemons. These interfaces are trivial to implement. To + simplify porting we provide this reference implementation. + Applications are welcome to reimplement the algorithms described + here if they do not want to include these two source files. + + The following functionality is provided: + + - Support for logging with log levels on stderr + - File descriptor passing for socket-based activation + - Daemon startup and status notification + - Detection of systemd boots + + You may compile this with -DDISABLE_SYSTEMD to disable systemd + support. This makes all those calls NOPs that are directly related to + systemd (i.e. only sd_is_xxx() will stay useful). + + Since this is drop-in code we don't want any of our symbols to be + exported in any case. Hence we declare hidden visibility for all of + them. + + You may find an up-to-date version of these source files online: + + http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h + http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c + + This should compile on non-Linux systems, too, but with the + exception of the sd_is_xxx() calls all functions will become NOPs. + + See sd-daemon(7) for more information. +*/ + +#if __GNUC__ >= 4 +#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) +#define _sd_hidden_ __attribute__ ((visibility("hidden"))) +#else +#define _sd_printf_attr_(a,b) +#define _sd_hidden_ +#endif + +/* + Log levels for usage on stderr: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + This is similar to printk() usage in the kernel. +*/ +#define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + +/* The first passed file descriptor is fd 3 */ +#define SD_LISTEN_FDS_START 3 + +/* + Returns how many file descriptors have been passed, or a negative + errno code on failure. Optionally, removes the $LISTEN_FDS and + $LISTEN_PID file descriptors from the environment (recommended, but + problematic in threaded environments). If r is the return value of + this function you'll find the file descriptors passed as fds + SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative + errno style error code on failure. This function call ensures that + the FD_CLOEXEC flag is set for the passed file descriptors, to make + sure they are not passed on to child processes. If FD_CLOEXEC shall + not be set, the caller needs to unset it after this call for all file + descriptors that are used. + + See sd_listen_fds(3) for more information. +*/ +int sd_listen_fds(int unset_environment) _sd_hidden_; + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a FIFO in the file system stored under the + specified path, 0 otherwise. If path is NULL a path name check will + not be done and the call only verifies if the file descriptor + refers to a FIFO. Returns a negative errno style error code on + failure. + + See sd_is_fifo(3) for more information. +*/ +int sd_is_fifo(int fd, const char *path) _sd_hidden_; + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a socket of the specified family (AF_INET, + ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If + family is 0 a socket family check will not be done. If type is 0 a + socket type check will not be done and the call only verifies if + the file descriptor refers to a socket. If listening is > 0 it is + verified that the socket is in listening mode. (i.e. listen() has + been called) If listening is == 0 it is verified that the socket is + not in listening mode. If listening is < 0 no listening mode check + is done. Returns a negative errno style error code on failure. + + See sd_is_socket(3) for more information. +*/ +int sd_is_socket(int fd, int family, int type, int listening) _sd_hidden_; + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an Internet socket, of the specified family + (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, + SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version + check is not done. If type is 0 a socket type check will not be + done. If port is 0 a socket port check will not be done. The + listening flag is used the same way as in sd_is_socket(). Returns a + negative errno style error code on failure. + + See sd_is_socket_inet(3) for more information. +*/ +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) _sd_hidden_; + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is an AF_UNIX socket of the specified type + (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 + a socket type check will not be done. If path is NULL a socket path + check will not be done. For normal AF_UNIX sockets set length to + 0. For abstract namespace sockets set length to the length of the + socket name (including the initial 0 byte), and pass the full + socket path in path (including the initial 0 byte). The listening + flag is used the same way as in sd_is_socket(). Returns a negative + errno style error code on failure. + + See sd_is_socket_unix(3) for more information. +*/ +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) _sd_hidden_; + +/* + Informs systemd about changed daemon state. This takes a number of + newline seperated environment-style variable assignments in a + string. The following variables are known: + + READY=1 Tells systemd that daemon startup is finished (only + relevant for services of Type=notify). The passed + argument is a boolean "1" or "0". Since there is + little value in signalling non-readiness the only + value daemons should send is "READY=1". + + STATUS=... Passes a single-line status string back to systemd + that describes the daemon state. This is free-from + and can be used for various purposes: general state + feedback, fsck-like programs could pass completion + percentages and failing programs could pass a human + readable error message. Example: "STATUS=Completed + 66% of file system check..." + + ERRNO=... If a daemon fails, the errno-style error code, + formatted as string. Example: "ERRNO=2" for ENOENT. + + BUSERROR=... If a daemon fails, the D-Bus error-style error + code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + + MAINPID=... The main pid of a daemon, in case systemd did not + fork off the process itself. Example: "MAINPID=4711" + + Daemons can choose to send additional variables. However, it is + recommened to prefix variable names not listed above with X_. + + Returns a negative errno-style error code on failure. Returns > 0 + if systemd could be notified, 0 if it couldn't possibly because + systemd is not running. + + Example: When a daemon finished starting up, it could issue this + call to notify systemd about it: + + sd_notify(0, "READY=1"); + + See sd_notifyf() for more complete examples. + + See sd_notify(3) for more information. +*/ +int sd_notify(int unset_environment, const char *state) _sd_hidden_; + +/* + Similar to sd_notify() but takes a format string. + + Example 1: A daemon could send the following after initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + Example 2: A daemon could send the following shortly before + exiting, on failure: + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror(errno), + errno); + + See sd_notifyf(3) for more information. +*/ +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_; + +/* + Returns > 0 if the system was booted with systemd. Returns < 0 on + error. Returns 0 if the system was not booted with systemd. Note + that all of the functions above handle non-systemd boots just + fine. You should NOT protect them with a call to this function. Also + note that this function checks whether the system, not the user + session is controlled by systemd. However the functions above work + for both session and system services. + + See sd_booted(3) for more information. +*/ +int sd_booted(void) _sd_hidden_; + +#ifdef __cplusplus +} +#endif + +#endif