void bus_watch_directory (const char *dir, void *userdata) { int fd; _dbus_assert (dir != NULL); if (num_fds >= MAX_DIRS_TO_WATCH ) { _dbus_warn ("Cannot watch config directory '%s'. Already watching %d directories\n", dir, MAX_DIRS_TO_WATCH); goto out; } fd = open (dir, O_RDONLY); if (fd < 0) { _dbus_warn ("Cannot open directory '%s'; error '%s'\n", dir, _dbus_strerror (errno)); goto out; } if (fcntl (fd, F_NOTIFY, DN_DELETE|DN_RENAME|DN_MODIFY) == -1) { _dbus_warn ("Cannot setup D_NOTIFY for '%s' error '%s'\n", dir, _dbus_strerror (errno)); close (fd); goto out; } fds[num_fds++] = fd; _dbus_verbose ("Added watch on config directory '%s'\n", dir); out: ; }
/** * Changes the user and group the bus is running as. * * @param user the user to become * @param error return location for errors * @returns #FALSE on failure */ dbus_bool_t _dbus_change_to_daemon_user (const char *user, DBusError *error) { dbus_uid_t uid; dbus_gid_t gid; DBusString u; _dbus_string_init_const (&u, user); if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid)) { dbus_set_error (error, DBUS_ERROR_FAILED, "User '%s' does not appear to exist?", user); return FALSE; } /* 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. * * 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; }
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)); _dbus_close (fd, NULL); 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; }
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; }
static void set_error_from_query_errno (DBusError *error, int error_number) { dbus_set_error (error, _dbus_error_from_errno (error_number), "Failed to query AppArmor policy: %s", _dbus_strerror (error_number)); }
/** * Sets the #DBusError with an explanation of why the spawned * child process exited (on a signal, or whatever). If * the child process has not exited, does nothing (error * will remain unset). * * @param sitter the babysitter * @param error an error to fill in */ void _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter, DBusError *error) { PING(); if (!_dbus_babysitter_get_child_exited (sitter)) return; PING(); if (sitter->have_spawn_errno) { dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, "Failed to execute program %s: %s", sitter->executable, _dbus_strerror (sitter->spawn_errno)); } else if (sitter->have_child_status) { PING(); dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, "Process %s exited with status %d", sitter->executable, sitter->child_status); } else { PING(); dbus_set_error (error, DBUS_ERROR_FAILED, "Process %s exited, status unknown", sitter->executable); } PING(); }
/** * Open the libaudit fd if appropriate. */ void bus_audit_init (BusContext *context) { #ifdef HAVE_LIBAUDIT int i; capng_get_caps_process (); /* Work around a bug in libcap-ng < 0.7.7: it leaks a fd, which isn't * close-on-exec. Assume it will be one of the first few fds. */ for (i = 3; i < 42; i++) _dbus_fd_set_close_on_exec (i); if (!capng_have_capability (CAPNG_EFFECTIVE, CAP_AUDIT_WRITE)) return; audit_fd = audit_open (); if (audit_fd < 0) { int e = errno; /* If kernel doesn't support audit, bail out */ if (e == EINVAL || e == EPROTONOSUPPORT || e == EAFNOSUPPORT) return; bus_context_log (context, DBUS_SYSTEM_LOG_WARNING, "Failed to open connection to the audit subsystem: %s", _dbus_strerror (e)); } #endif /* HAVE_LIBAUDIT */ }
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; }
EXPORT_C #endif void _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter, DBusError *error) { if (!_dbus_babysitter_get_child_exited (sitter)) return; /* Note that if exec fails, we will also get a child status * from the babysitter saying the child exited, * so we need to give priority to the exec error */ if (sitter->have_exec_errnum) { dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, "Failed to execute program %s: %s", sitter->executable, _dbus_strerror (sitter->errnum)); } else if (sitter->have_fork_errnum) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, "Failed to fork a new process %s: %s", sitter->executable, _dbus_strerror (sitter->errnum)); } else if (sitter->have_child_status) { if (WIFEXITED (sitter->status)) dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, "Process %s exited with status %d", sitter->executable, WEXITSTATUS (sitter->status)); else if (WIFSIGNALED (sitter->status)) dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED, "Process %s received signal %d", sitter->executable, WTERMSIG (sitter->status)); else dbus_set_error (error, DBUS_ERROR_FAILED, "Process %s exited abnormally", sitter->executable); } else { dbus_set_error (error, DBUS_ERROR_FAILED, "Process %s exited, reason unknown", sitter->executable); } }
dbus_bool_t _dbus_directory_get_next_file (DBusDirIter *iter, DBusString *filename, DBusError *error) { struct dirent *d, *ent; size_t buf_size; int err; _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (!dirent_buf_size (iter->d, &buf_size)) { dbus_set_error (error, DBUS_ERROR_FAILED, "Can't calculate buffer size when reading directory"); return FALSE; } d = (struct dirent *)dbus_malloc (buf_size); if (!d) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, "No memory to read directory entry"); return FALSE; } again: err = readdir_r (iter->d, d, &ent); if (err || !ent) { if (err != 0) dbus_set_error (error, _dbus_error_from_errno (err), "%s", _dbus_strerror (err)); dbus_free (d); 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"); dbus_free (d); return FALSE; } else { dbus_free (d); return TRUE; } } }
static void check_babysit_events (pid_t grandchild_pid, int parent_pipe, int revents) { pid_t ret; int status; do { ret = waitpid (grandchild_pid, &status, WNOHANG); /* The man page says EINTR can't happen with WNOHANG, * but there are reports of it (maybe only with valgrind?) */ } while (ret < 0 && errno == EINTR); if (ret == 0) { _dbus_verbose ("no child exited\n"); ; /* no child exited */ } else if (ret < 0) { /* This isn't supposed to happen. */ _dbus_warn ("unexpected waitpid() failure in check_babysit_events(): %s", _dbus_strerror (errno)); exit (1); } else if (ret == grandchild_pid) { /* Child exited */ _dbus_verbose ("reaped child pid %ld\n", (long) ret); write_status_and_exit (parent_pipe, status); } else { _dbus_warn ("waitpid() reaped pid %d that we've never heard of", (int) ret); exit (1); } if (revents & _DBUS_POLLIN) { _dbus_verbose ("babysitter got POLLIN from parent pipe\n"); } if (revents & (_DBUS_POLLERR | _DBUS_POLLHUP)) { /* Parent is gone, so we just exit */ _dbus_verbose ("babysitter got POLLERR or POLLHUP from parent\n"); exit (0); } }
static ReadStatus read_ints (int fd, int *buf, int n_ints_in_buf, int *n_ints_read, DBusError *error) { size_t bytes = 0; ReadStatus retval; _DBUS_ASSERT_ERROR_IS_CLEAR (error); retval = READ_STATUS_OK; while (TRUE) { ssize_t chunk; size_t to_read; to_read = sizeof (int) * n_ints_in_buf - bytes; if (to_read == 0) break; again: chunk = read (fd, ((char*)buf) + bytes, to_read); if (chunk < 0 && errno == EINTR) goto again; if (chunk < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FAILED, "Failed to read from child pipe (%s)", _dbus_strerror (errno)); retval = READ_STATUS_ERROR; break; } else if (chunk == 0) { retval = READ_STATUS_EOF; break; /* EOF */ } else /* chunk > 0 */ bytes += chunk; } *n_ints_read = (int)(bytes / sizeof(int)); return retval; }
/* return value is whether we successfully read any new data. */ static dbus_bool_t read_data_into_auth (DBusTransport *transport, dbus_bool_t *oom) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusString *buffer; int bytes_read; int saved_errno; *oom = FALSE; _dbus_auth_get_buffer (transport->auth, &buffer); bytes_read = _dbus_read_socket (socket_transport->fd, buffer, socket_transport->max_bytes_read_per_iteration); saved_errno = _dbus_save_socket_errno (); _dbus_auth_return_buffer (transport->auth, buffer); if (bytes_read > 0) { _dbus_verbose (" read %d bytes in auth phase\n", bytes_read); return TRUE; } else if (bytes_read < 0) { /* EINTR already handled for us */ if (_dbus_get_is_errno_enomem (saved_errno)) { *oom = TRUE; } else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) ; /* do nothing, just return FALSE below */ else { _dbus_verbose ("Error reading from remote app: %s\n", _dbus_strerror (saved_errno)); do_io_error (transport); } return FALSE; } else { _dbus_assert (bytes_read == 0); _dbus_verbose ("Disconnected from remote app\n"); do_io_error (transport); return FALSE; } }
/** * 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. * * 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; } _dbus_warn("%s,%d:\n",__func__,__LINE__); return TRUE; }
/** * Verify that the config mode is compatible with the kernel's AppArmor * support. If AppArmor mediation will be enabled, determine the bus * confinement label. */ dbus_bool_t bus_apparmor_full_init (DBusError *error) { #ifdef HAVE_APPARMOR char *label, *mode; if (apparmor_enabled) { if (apparmor_config_mode == APPARMOR_DISABLED) { apparmor_enabled = FALSE; return TRUE; } if (bus_con == NULL) { if (aa_getcon (&label, &mode) == -1) { dbus_set_error (error, DBUS_ERROR_FAILED, "Error getting AppArmor context of bus: %s", _dbus_strerror (errno)); return FALSE; } bus_con = bus_apparmor_confinement_new (label, mode); if (bus_con == NULL) { BUS_SET_OOM (error); free (label); return FALSE; } } } else { if (apparmor_config_mode == APPARMOR_REQUIRED) { dbus_set_error (error, DBUS_ERROR_FAILED, "AppArmor mediation required but not present"); return FALSE; } else if (apparmor_config_mode == APPARMOR_ENABLED) { return TRUE; } } #endif return TRUE; }
/** * Creates a VMCI stream socket connected to the given cid and port. * The connection fd is returned, and is set up as nonblocking. * * This will set FD_CLOEXEC for the socket returned. * * @param path the path to UNIX domain socket * @param abstract #TRUE to use abstract namespace * @param error return location for error code * @returns connection file descriptor or -1 on error */ static int _dbus_connect_vmci (const char *cid_str, const char *port_str, DBusError *error) { int fd; size_t path_len; struct sockaddr_vm addr; unsigned int af_vmci, cid, port; _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_verbose ("connecting to VMCI stream socket %s:%s\n", cid_str, port_str); cid = atoi(cid_str); port = atoi(port_str); if ((af_vmci = VMCISock_GetAFValue()) < 0) { assert(0); } if (_dbus_open_socket(&fd, af_vmci, SOCK_STREAM, 0, error) < 0) { return fd; } _DBUS_ZERO (addr); addr.svm_family = af_vmci; addr.svm_cid = cid; addr.svm_port = port; if (connect (fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to connect to VMCI (cid, port): %s:%s: %s", cid_str, port_str, _dbus_strerror (errno)); _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; return -1; } return fd; }
static dbus_bool_t make_pipe (int p[2], DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (pipe (p) < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FAILED, "Failed to create pipe for communicating with child process (%s)", _dbus_strerror (errno)); return FALSE; } return TRUE; }
static int _init_kqueue (BusContext *context) { int ret = 0; if (kq < 0) { kq = kqueue (); if (kq < 0) { _dbus_warn ("Cannot create kqueue; error '%s'\n", _dbus_strerror (errno)); goto out; } loop = bus_context_get_loop (context); watch = _dbus_watch_new (kq, DBUS_WATCH_READABLE, TRUE, _handle_kqueue_watch, NULL, NULL); if (watch == NULL) { _dbus_warn ("Unable to create kqueue watch\n"); close (kq); kq = -1; goto out; } if (!_dbus_loop_add_watch (loop, watch)) { _dbus_warn ("Unable to add reload watch to main loop"); _dbus_watch_invalidate (watch); _dbus_watch_unref (watch); watch = NULL; close (kq); kq = -1; goto out; } } ret = 1; out: return ret; }
BusAppArmorConfinement* bus_apparmor_init_connection_confinement (DBusConnection *connection, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *confinement; char *label, *mode; int fd; if (!apparmor_enabled) return NULL; _dbus_assert (connection != NULL); if (!dbus_connection_get_socket (connection, &fd)) { dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to get socket file descriptor of connection"); return NULL; } if (aa_getpeercon (fd, &label, &mode) == -1) { if (errno == ENOMEM) BUS_SET_OOM (error); else dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to get AppArmor confinement information of socket peer: %s", _dbus_strerror (errno)); return NULL; } confinement = bus_apparmor_confinement_new (label, mode); if (confinement == NULL) { BUS_SET_OOM (error); free (label); return NULL; } return confinement; #else return NULL; #endif /* HAVE_APPARMOR */ }
/** * write data to a pipe. * * @param pipe the pipe instance * @param buffer the buffer to write data from * @param start the first byte in the buffer to write * @param len the number of bytes to try to write * @param error error return * @returns the number of bytes written or -1 on error */ int _dbus_pipe_write (DBusPipe *pipe, const DBusString *buffer, int start, int len, DBusError *error) { int written; written = _dbus_write (pipe->fd, buffer, start, len); if (written < 0) { dbus_set_error (error, DBUS_ERROR_FAILED, "Writing to pipe: %s\n", _dbus_strerror (errno)); } return written; }
/** * 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. * * This function is not re-entrant, and not necessarily thread-safe. * Only use it for test code or single-threaded utilities. * * @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; int err; _DBUS_ASSERT_ERROR_IS_CLEAR (error); again: errno = 0; ent = readdir (iter->d); if (!ent) { err = errno; if (err != 0) dbus_set_error (error, _dbus_error_from_errno (err), "%s", _dbus_strerror (err)); 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; } } }
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; }
static dbus_bool_t _dbus_open_socket (int *fd_p, int domain, int type, int protocol, DBusError *error) { #ifdef SOCK_CLOEXEC dbus_bool_t cloexec_done; *fd_p = socket (domain, type | SOCK_CLOEXEC, protocol); cloexec_done = *fd_p >= 0; /* Check if kernel seems to be too old to know SOCK_CLOEXEC */ if (*fd_p < 0 && errno == EINVAL) #endif { *fd_p = socket (domain, type, protocol); } if (*fd_p >= 0) { #ifdef SOCK_CLOEXEC if (!cloexec_done) #endif { _dbus_fd_set_close_on_exec(*fd_p); } _dbus_verbose ("socket fd %d opened\n", *fd_p); return TRUE; } else { dbus_set_error(error, _dbus_error_from_errno (errno), "Failed to open socket: %s", _dbus_strerror (errno)); return FALSE; } }
static dbus_bool_t make_pipe (int p[2], DBusError *error) { int retval; #ifdef HAVE_PIPE2 dbus_bool_t cloexec_done; retval = pipe2 (p, O_CLOEXEC); cloexec_done = retval >= 0; /* Check if kernel seems to be too old to know pipe2(). We assume that if pipe2 is available, O_CLOEXEC is too. */ if (retval < 0 && errno == ENOSYS) #endif { retval = pipe(p); } _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (retval < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FAILED, "Failed to create pipe for communicating with child process (%s)", _dbus_strerror (errno)); return FALSE; } #ifdef HAVE_PIPE2 if (!cloexec_done) #endif { _dbus_fd_set_close_on_exec (p[0]); _dbus_fd_set_close_on_exec (p[1]); } return TRUE; }
/* Return value is whether we successfully wrote any bytes */ static dbus_bool_t write_data_from_auth (DBusTransport *transport) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; int bytes_written; int saved_errno; const DBusString *buffer; if (!_dbus_auth_get_bytes_to_send (transport->auth, &buffer)) return FALSE; bytes_written = _dbus_write_socket (socket_transport->fd, buffer, 0, _dbus_string_get_length (buffer)); saved_errno = _dbus_save_socket_errno (); if (bytes_written > 0) { _dbus_auth_bytes_sent (transport->auth, bytes_written); return TRUE; } else if (bytes_written < 0) { /* EINTR already handled for us */ if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) ; else { _dbus_verbose ("Error writing to remote app: %s\n", _dbus_strerror (saved_errno)); do_io_error (transport); } } return FALSE; }
void bus_set_watched_dirs (BusContext *context, DBusList **directories) { int new_fds[MAX_DIRS_TO_WATCH]; char *new_dirs[MAX_DIRS_TO_WATCH]; DBusList *link; int i, j, fd; struct kevent ev; #ifdef O_CLOEXEC dbus_bool_t cloexec_done = 0; #endif if (!_init_kqueue (context)) goto out; for (i = 0; i < MAX_DIRS_TO_WATCH; i++) { new_fds[i] = -1; new_dirs[i] = NULL; } i = 0; link = _dbus_list_get_first_link (directories); while (link != NULL) { new_dirs[i++] = (char *)link->data; link = _dbus_list_get_next_link (directories, link); } /* Look for directories in both the old and new sets, if * we find one, move its data into the new set. */ for (i = 0; new_dirs[i]; i++) { for (j = 0; j < num_fds; j++) { if (dirs[j] && strcmp (new_dirs[i], dirs[j]) == 0) { new_fds[i] = fds[j]; new_dirs[i] = dirs[j]; fds[j] = -1; dirs[j] = NULL; break; } } } /* Any directory we find in "fds" with a nonzero fd must * not be in the new set, so perform cleanup now. */ for (j = 0; j < num_fds; j++) { if (fds[j] != -1) { close (fds[j]); dbus_free (dirs[j]); fds[j] = -1; dirs[j] = NULL; } } for (i = 0; new_dirs[i]; i++) { if (new_fds[i] == -1) { /* FIXME - less lame error handling for failing to add a watch; * we may need to sleep. */ #ifdef O_CLOEXEC fd = open (new_dirs[i], O_RDONLY | O_CLOEXEC); cloexec_done = (fd >= 0); if (fd < 0 && errno == EINVAL) #endif { fd = open (new_dirs[i], O_RDONLY); } if (fd < 0) { if (errno != ENOENT) { _dbus_warn ("Cannot open directory '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno)); goto out; } else { new_fds[i] = -1; new_dirs[i] = NULL; continue; } } #ifdef O_CLOEXEC if (!cloexec_done) #endif { _dbus_fd_set_close_on_exec (fd); } EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_RENAME, 0, 0); if (kevent (kq, &ev, 1, NULL, 0, NULL) == -1) { _dbus_warn ("Cannot setup a kevent for '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno)); close (fd); goto out; } new_fds[i] = fd; new_dirs[i] = _dbus_strdup (new_dirs[i]); if (!new_dirs[i]) { /* FIXME have less lame handling for OOM, we just silently fail to * watch. (In reality though, the whole OOM handling in dbus is * stupid but we won't go into that in this comment =) ) */ close (fd); new_fds[i] = -1; } } } num_fds = i; for (i = 0; i < MAX_DIRS_TO_WATCH; i++) { fds[i] = new_fds[i]; dirs[i] = new_dirs[i]; } out: ; }
static int _init_kqueue (BusContext *context) { if (kq < 0) { kq = kqueue (); if (kq < 0) { _dbus_warn ("Cannot create kqueue; error '%s'\n", _dbus_strerror (errno)); goto out; } loop = bus_context_get_loop (context); _dbus_loop_ref (loop); watch = _dbus_watch_new (kq, DBUS_WATCH_READABLE, TRUE, _handle_kqueue_watch, NULL, NULL); if (watch == NULL) { _dbus_warn ("Unable to create kqueue watch\n"); goto out1; } if (!_dbus_loop_add_watch (loop, watch)) { _dbus_warn ("Unable to add reload watch to main loop"); goto out2; } if (!_dbus_register_shutdown_func (_shutdown_kqueue, NULL)) { _dbus_warn ("Unable to register shutdown function"); goto out3; } } return 1; out3: _dbus_loop_remove_watch (loop, watch); out2: if (watch) { _dbus_watch_invalidate (watch); _dbus_watch_unref (watch); watch = NULL; } out1: if (kq >= 0) { close (kq); kq = -1; } if (loop) { _dbus_loop_unref (loop); loop = NULL; } out: return 0; }
/* returns false on out-of-memory */ static dbus_bool_t do_reading (DBusTransport *transport) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusString *buffer; int bytes_read; int total; dbus_bool_t oom; int saved_errno; _dbus_verbose ("fd = %" DBUS_SOCKET_FORMAT "\n", _dbus_socket_printable (socket_transport->fd)); /* No messages without authentication! */ if (!_dbus_transport_try_to_authenticate (transport)) return TRUE; oom = FALSE; total = 0; again: /* See if we've exceeded max messages and need to disable reading */ check_read_watch (transport); if (total > socket_transport->max_bytes_read_per_iteration) { _dbus_verbose ("%d bytes exceeds %d bytes read per iteration, returning\n", total, socket_transport->max_bytes_read_per_iteration); goto out; } _dbus_assert (socket_transport->read_watch != NULL || transport->disconnected); if (transport->disconnected) goto out; if (!dbus_watch_get_enabled (socket_transport->read_watch)) return TRUE; if (_dbus_auth_needs_decoding (transport->auth)) { /* Does fd passing even make sense with encoded data? */ _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0) bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming); else bytes_read = _dbus_read_socket (socket_transport->fd, &socket_transport->encoded_incoming, socket_transport->max_bytes_read_per_iteration); saved_errno = _dbus_save_socket_errno (); _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) == bytes_read); if (bytes_read > 0) { _dbus_message_loader_get_buffer (transport->loader, &buffer); if (!_dbus_auth_decode_data (transport->auth, &socket_transport->encoded_incoming, buffer)) { _dbus_verbose ("Out of memory decoding incoming data\n"); _dbus_message_loader_return_buffer (transport->loader, buffer); oom = TRUE; goto out; } _dbus_message_loader_return_buffer (transport->loader, buffer); _dbus_string_set_length (&socket_transport->encoded_incoming, 0); _dbus_string_compact (&socket_transport->encoded_incoming, 2048); } } else { _dbus_message_loader_get_buffer (transport->loader, &buffer); #ifdef HAVE_UNIX_FD_PASSING if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)) { int *fds; unsigned int n_fds; if (!_dbus_message_loader_get_unix_fds(transport->loader, &fds, &n_fds)) { _dbus_verbose ("Out of memory reading file descriptors\n"); _dbus_message_loader_return_buffer (transport->loader, buffer); oom = TRUE; goto out; } bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd, buffer, socket_transport->max_bytes_read_per_iteration, fds, &n_fds); saved_errno = _dbus_save_socket_errno (); if (bytes_read >= 0 && n_fds > 0) _dbus_verbose("Read %i unix fds\n", n_fds); _dbus_message_loader_return_unix_fds(transport->loader, fds, bytes_read < 0 ? 0 : n_fds); } else #endif { bytes_read = _dbus_read_socket (socket_transport->fd, buffer, socket_transport->max_bytes_read_per_iteration); saved_errno = _dbus_save_socket_errno (); } _dbus_message_loader_return_buffer (transport->loader, buffer); } if (bytes_read < 0) { /* EINTR already handled for us */ if (_dbus_get_is_errno_enomem (saved_errno)) { _dbus_verbose ("Out of memory in read()/do_reading()\n"); oom = TRUE; goto out; } else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) goto out; else { _dbus_verbose ("Error reading from remote app: %s\n", _dbus_strerror (saved_errno)); do_io_error (transport); goto out; } } else if (bytes_read == 0) { _dbus_verbose ("Disconnected from remote app\n"); do_io_error (transport); goto out; } else { _dbus_verbose (" read %d bytes\n", bytes_read); total += bytes_read; if (!_dbus_transport_queue_messages (transport)) { oom = TRUE; _dbus_verbose (" out of memory when queueing messages we just read in the transport\n"); goto out; } /* Try reading more data until we get EAGAIN and return, or * exceed max bytes per iteration. If in blocking mode of * course we'll block instead of returning. */ goto again; } out: if (oom) return FALSE; else return TRUE; }
/* returns false on oom */ static dbus_bool_t do_writing (DBusTransport *transport) { int total; DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; dbus_bool_t oom; /* No messages without authentication! */ if (!_dbus_transport_try_to_authenticate (transport)) { _dbus_verbose ("Not authenticated, not writing anything\n"); return TRUE; } if (transport->disconnected) { _dbus_verbose ("Not connected, not writing anything\n"); return TRUE; } #if 1 _dbus_verbose ("do_writing(), have_messages = %d, fd = %" DBUS_SOCKET_FORMAT "\n", _dbus_connection_has_messages_to_send_unlocked (transport->connection), _dbus_socket_printable (socket_transport->fd)); #endif oom = FALSE; total = 0; while (!transport->disconnected && _dbus_connection_has_messages_to_send_unlocked (transport->connection)) { int bytes_written; DBusMessage *message; const DBusString *header; const DBusString *body; int header_len, body_len; int total_bytes_to_write; int saved_errno; if (total > socket_transport->max_bytes_written_per_iteration) { _dbus_verbose ("%d bytes exceeds %d bytes written per iteration, returning\n", total, socket_transport->max_bytes_written_per_iteration); goto out; } message = _dbus_connection_get_message_to_send (transport->connection); _dbus_assert (message != NULL); dbus_message_lock (message); #if 0 _dbus_verbose ("writing message %p\n", message); #endif _dbus_message_get_network_data (message, &header, &body); header_len = _dbus_string_get_length (header); body_len = _dbus_string_get_length (body); if (_dbus_auth_needs_encoding (transport->auth)) { /* Does fd passing even make sense with encoded data? */ _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0) { if (!_dbus_auth_encode_data (transport->auth, header, &socket_transport->encoded_outgoing)) { oom = TRUE; goto out; } if (!_dbus_auth_encode_data (transport->auth, body, &socket_transport->encoded_outgoing)) { _dbus_string_set_length (&socket_transport->encoded_outgoing, 0); oom = TRUE; goto out; } } total_bytes_to_write = _dbus_string_get_length (&socket_transport->encoded_outgoing); #if 0 _dbus_verbose ("encoded message is %d bytes\n", total_bytes_to_write); #endif bytes_written = _dbus_write_socket (socket_transport->fd, &socket_transport->encoded_outgoing, socket_transport->message_bytes_written, total_bytes_to_write - socket_transport->message_bytes_written); saved_errno = _dbus_save_socket_errno (); } else { total_bytes_to_write = header_len + body_len; #if 0 _dbus_verbose ("message is %d bytes\n", total_bytes_to_write); #endif #ifdef HAVE_UNIX_FD_PASSING if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)) { /* Send the fds along with the first byte of the message */ const int *unix_fds; unsigned n; _dbus_message_get_unix_fds(message, &unix_fds, &n); bytes_written = _dbus_write_socket_with_unix_fds_two (socket_transport->fd, header, socket_transport->message_bytes_written, header_len - socket_transport->message_bytes_written, body, 0, body_len, unix_fds, n); saved_errno = _dbus_save_socket_errno (); if (bytes_written > 0 && n > 0) _dbus_verbose("Wrote %i unix fds\n", n); } else #endif { if (socket_transport->message_bytes_written < header_len) { bytes_written = _dbus_write_socket_two (socket_transport->fd, header, socket_transport->message_bytes_written, header_len - socket_transport->message_bytes_written, body, 0, body_len); } else { bytes_written = _dbus_write_socket (socket_transport->fd, body, (socket_transport->message_bytes_written - header_len), body_len - (socket_transport->message_bytes_written - header_len)); } saved_errno = _dbus_save_socket_errno (); } } if (bytes_written < 0) { /* EINTR already handled for us */ /* If the other end closed the socket with close() or shutdown(), we * receive EPIPE here but we must not close the socket yet: there * might still be some data to read. See: * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html */ if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) || _dbus_get_is_errno_epipe (saved_errno)) goto out; /* Since Linux commit 25888e (from 2.6.37-rc4, Nov 2010), sendmsg() * on Unix sockets returns -1 errno=ETOOMANYREFS when the passfd * mechanism (SCM_RIGHTS) is used recursively with a recursion level * of maximum 4. The kernel does not have an API to check whether * the passed fds can be forwarded and it can change asynchronously. * See: * https://bugs.freedesktop.org/show_bug.cgi?id=80163 */ else if (_dbus_get_is_errno_etoomanyrefs (saved_errno)) { /* We only send fds in the first byte of the message. * ETOOMANYREFS cannot happen after. */ _dbus_assert (socket_transport->message_bytes_written == 0); _dbus_verbose (" discard message of %d bytes due to ETOOMANYREFS\n", total_bytes_to_write); socket_transport->message_bytes_written = 0; _dbus_string_set_length (&socket_transport->encoded_outgoing, 0); _dbus_string_compact (&socket_transport->encoded_outgoing, 2048); /* The message was not actually sent but it needs to be removed * from the outgoing queue */ _dbus_connection_message_sent_unlocked (transport->connection, message); } else { _dbus_verbose ("Error writing to remote app: %s\n", _dbus_strerror (saved_errno)); do_io_error (transport); goto out; } } else { _dbus_verbose (" wrote %d bytes of %d\n", bytes_written, total_bytes_to_write); total += bytes_written; socket_transport->message_bytes_written += bytes_written; _dbus_assert (socket_transport->message_bytes_written <= total_bytes_to_write); if (socket_transport->message_bytes_written == total_bytes_to_write) { socket_transport->message_bytes_written = 0; _dbus_string_set_length (&socket_transport->encoded_outgoing, 0); _dbus_string_compact (&socket_transport->encoded_outgoing, 2048); _dbus_connection_message_sent_unlocked (transport->connection, message); } } } out: if (oom) return FALSE; else return TRUE; }
/** * @todo We need to have a way to wake up the select sleep if * a new iteration request comes in with a flag (read/write) that * we're not currently serving. Otherwise a call that just reads * could block a write call forever (if there are no incoming * messages). */ static void socket_do_iteration (DBusTransport *transport, unsigned int flags, int timeout_milliseconds) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusPollFD poll_fd; int poll_res; int poll_timeout; _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %" DBUS_SOCKET_FORMAT "\n", flags & DBUS_ITERATION_DO_READING ? "read" : "", flags & DBUS_ITERATION_DO_WRITING ? "write" : "", timeout_milliseconds, socket_transport->read_watch, socket_transport->write_watch, _dbus_socket_printable (socket_transport->fd)); /* the passed in DO_READING/DO_WRITING flags indicate whether to * read/write messages, but regardless of those we may need to block * for reading/writing to do auth. But if we do reading for auth, * we don't want to read any messages yet if not given DO_READING. */ poll_fd.fd = _dbus_socket_get_pollable (socket_transport->fd); poll_fd.events = 0; if (_dbus_transport_try_to_authenticate (transport)) { /* This is kind of a hack; if we have stuff to write, then try * to avoid the poll. This is probably about a 5% speedup on an * echo client/server. * * If both reading and writing were requested, we want to avoid this * since it could have funky effects: * - both ends spinning waiting for the other one to read * data so they can finish writing * - prioritizing all writing ahead of reading */ if ((flags & DBUS_ITERATION_DO_WRITING) && !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) && !transport->disconnected && _dbus_connection_has_messages_to_send_unlocked (transport->connection)) { do_writing (transport); if (transport->disconnected || !_dbus_connection_has_messages_to_send_unlocked (transport->connection)) goto out; } /* If we get here, we decided to do the poll() after all */ _dbus_assert (socket_transport->read_watch); if (flags & DBUS_ITERATION_DO_READING) poll_fd.events |= _DBUS_POLLIN; _dbus_assert (socket_transport->write_watch); if (flags & DBUS_ITERATION_DO_WRITING) poll_fd.events |= _DBUS_POLLOUT; } else { DBusAuthState auth_state; auth_state = _dbus_auth_do_work (transport->auth); if (transport->receive_credentials_pending || auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) poll_fd.events |= _DBUS_POLLIN; if (transport->send_credentials_pending || auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) poll_fd.events |= _DBUS_POLLOUT; } if (poll_fd.events) { int saved_errno; if (flags & DBUS_ITERATION_BLOCK) poll_timeout = timeout_milliseconds; else poll_timeout = 0; /* For blocking selects we drop the connection lock here * to avoid blocking out connection access during a potentially * indefinite blocking call. The io path is still protected * by the io_path_cond condvar, so we won't reenter this. */ if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("unlock pre poll\n"); _dbus_connection_unlock (transport->connection); } again: poll_res = _dbus_poll (&poll_fd, 1, poll_timeout); saved_errno = _dbus_save_socket_errno (); if (poll_res < 0 && _dbus_get_is_errno_eintr (saved_errno)) goto again; if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("lock post poll\n"); _dbus_connection_lock (transport->connection); } if (poll_res >= 0) { if (poll_res == 0) poll_fd.revents = 0; /* some concern that posix does not guarantee this; * valgrind flags it as an error. though it probably * is guaranteed on linux at least. */ if (poll_fd.revents & _DBUS_POLLERR) do_io_error (transport); else { dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0; dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0; dbus_bool_t authentication_completed; _dbus_verbose ("in iteration, need_read=%d need_write=%d\n", need_read, need_write); do_authentication (transport, need_read, need_write, &authentication_completed); /* See comment in socket_handle_watch. */ if (authentication_completed) goto out; if (need_read && (flags & DBUS_ITERATION_DO_READING)) do_reading (transport); if (need_write && (flags & DBUS_ITERATION_DO_WRITING)) do_writing (transport); } } else { _dbus_verbose ("Error from _dbus_poll(): %s\n", _dbus_strerror (saved_errno)); } } out: /* We need to install the write watch only if we did not * successfully write everything. Note we need to be careful that we * don't call check_write_watch *before* do_writing, since it's * inefficient to add the write watch, and we can avoid it most of * the time since we can write immediately. * * However, we MUST always call check_write_watch(); DBusConnection code * relies on the fact that running an iteration will notice that * messages are pending. */ check_write_watch (transport); _dbus_verbose (" ... leaving do_iteration()\n"); }