static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) { Automount *a = AUTOMOUNT(userdata); _cleanup_(expire_data_freep) struct expire_data *data = NULL; int r; assert(a); assert(source == a->expire_event_source); data = new0(struct expire_data, 1); if (!data) return log_oom(); data->ioctl_fd = -1; data->dev_autofs_fd = fcntl(UNIT(a)->manager->dev_autofs_fd, F_DUPFD_CLOEXEC, 3); if (data->dev_autofs_fd < 0) return log_unit_error_errno(UNIT(a), errno, "Failed to duplicate autofs fd: %m"); data->ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id); if (data->ioctl_fd < 0) return log_unit_error_errno(UNIT(a), data->ioctl_fd, "Couldn't open autofs ioctl fd: %m"); r = asynchronous_job(expire_thread, data); if (r < 0) return log_unit_error_errno(UNIT(a), r, "Failed to start expire job: %m"); data = NULL; return automount_start_expire(a); }
static int device_add_udev_wants(Unit *u, struct udev_device *dev) { const char *wants, *property, *p; int r; assert(u); assert(dev); property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); for (p = wants;;) { _cleanup_free_ char *word = NULL, *k = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == 0) return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) return log_unit_error_errno(u, r, "Failed to add parse %s: %m", property); r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k); if (r < 0) return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word); r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true); if (r < 0) return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); } }
static int device_add_udev_wants(Unit *u, struct udev_device *dev) { const char *wants; const char *word, *state; size_t l; int r; const char *property; assert(u); assert(dev); property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); if (!wants) return 0; FOREACH_WORD_QUOTED(word, l, wants, state) { _cleanup_free_ char *n = NULL; char e[l+1]; memcpy(e, word, l); e[l] = 0; r = unit_name_mangle(e, UNIT_NAME_NOGLOB, &n); if (r < 0) return log_unit_error_errno(u, r, "Failed to mangle unit name: %m"); r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true); if (r < 0) return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); }
static int slice_verify(Slice *s) { _cleanup_free_ char *parent = NULL; int r; assert(s); if (UNIT(s)->load_state != UNIT_LOADED) return 0; if (!slice_name_is_valid(UNIT(s)->id)) { log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id); return -EINVAL; } r = slice_build_parent_slice(UNIT(s)->id, &parent); if (r < 0) return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m"); if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice)) { log_unit_error(UNIT(s), "Located outside of parent slice. Refusing."); return -EINVAL; } return 0; }
static int device_update_description(Unit *u, struct udev_device *dev, const char *path) { const char *model; int r; assert(u); assert(dev); assert(path); model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); if (!model) model = udev_device_get_property_value(dev, "ID_MODEL"); if (model) { const char *label; /* Try to concatenate the device model string with a label, if there is one */ label = udev_device_get_property_value(dev, "ID_FS_LABEL"); if (!label) label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"); if (!label) label = udev_device_get_property_value(dev, "ID_PART_ENTRY_NUMBER"); if (label) { _cleanup_free_ char *j; j = strjoin(model, " ", label); if (j) r = unit_set_description(u, j); else r = -ENOMEM; } else r = unit_set_description(u, model); } else r = unit_set_description(u, path); if (r < 0) log_unit_error_errno(u, r, "Failed to set device description: %m"); return r; }
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; union autofs_v5_packet_union packet; Automount *a = AUTOMOUNT(userdata); struct stat st; Unit *trigger; int r; assert(a); assert(fd == a->pipe_fd); if (events != EPOLLIN) { log_unit_error(UNIT(a), "Got invalid poll event %"PRIu32" on pipe (fd=%d)", events, fd); goto fail; } r = loop_read_exact(a->pipe_fd, &packet, sizeof(packet), true); if (r < 0) { log_unit_error_errno(UNIT(a), r, "Invalid read from pipe: %m"); goto fail; } switch (packet.hdr.type) { case autofs_ptype_missing_direct: if (packet.v5_packet.pid > 0) { _cleanup_free_ char *p = NULL; get_process_comm(packet.v5_packet.pid, &p); log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p)); } else log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where); r = set_ensure_allocated(&a->tokens, NULL); if (r < 0) { log_unit_error(UNIT(a), "Failed to allocate token set."); goto fail; } r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); if (r < 0) { log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); goto fail; } automount_enter_runnning(a); break; case autofs_ptype_expire_direct: log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); automount_stop_expire(a); r = set_ensure_allocated(&a->expire_tokens, NULL); if (r < 0) { log_unit_error(UNIT(a), "Failed to allocate token set."); goto fail; } r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); if (r < 0) { log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); goto fail; } /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) { log_unit_info(UNIT(a), "Automount point already unmounted?"); automount_send_ready(a, a->expire_tokens, 0); break; } trigger = UNIT_TRIGGER(UNIT(a)); if (!trigger) { log_unit_error(UNIT(a), "Unit to trigger vanished."); goto fail; } r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r)); goto fail; } break; default: log_unit_error(UNIT(a), "Received unknown automount request %i", packet.hdr.type); break; } return 0; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); return 0; }
static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Automount *a = AUTOMOUNT(u); int r; assert(a); assert(fds); if (streq(key, "state")) { AutomountState state; state = automount_state_from_string(value); if (state < 0) log_unit_debug(u, "Failed to parse state value: %s", value); else a->deserialized_state = state; } else if (streq(key, "result")) { AutomountResult f; f = automount_result_from_string(value); if (f < 0) log_unit_debug(u, "Failed to parse result value: %s", value); else if (f != AUTOMOUNT_SUCCESS) a->result = f; } else if (streq(key, "dev-id")) { unsigned d; if (safe_atou(value, &d) < 0) log_unit_debug(u, "Failed to parse dev-id value: %s", value); else a->dev_id = (unsigned) d; } else if (streq(key, "token")) { unsigned token; if (safe_atou(value, &token) < 0) log_unit_debug(u, "Failed to parse token value: %s", value); else { r = set_ensure_allocated(&a->tokens, NULL); if (r < 0) { log_oom(); return 0; } r = set_put(a->tokens, UINT_TO_PTR(token)); if (r < 0) log_unit_error_errno(u, r, "Failed to add token to set: %m"); } } else if (streq(key, "expire-token")) { unsigned token; if (safe_atou(value, &token) < 0) log_unit_debug(u, "Failed to parse token value: %s", value); else { r = set_ensure_allocated(&a->expire_tokens, NULL); if (r < 0) { log_oom(); return 0; } r = set_put(a->expire_tokens, UINT_TO_PTR(token)); if (r < 0) log_unit_error_errno(u, r, "Failed to add expire token to set: %m"); } } else if (streq(key, "pipe-fd")) { int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse pipe-fd value: %s", value); else { safe_close(a->pipe_fd); a->pipe_fd = fdset_remove(fds, fd); } } else log_unit_debug(u, "Unknown serialization key: %s", key); return 0; }
static void automount_enter_waiting(Automount *a) { _cleanup_close_ int ioctl_fd = -1; int p[2] = { -1, -1 }; char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1]; char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1 + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1]; bool mounted = false; int r, dev_autofs_fd; struct stat st; assert(a); assert(a->pipe_fd < 0); assert(a->where); set_clear(a->tokens); r = unit_fail_if_symlink(UNIT(a), a->where); if (r < 0) goto fail; (void) mkdir_p_label(a->where, 0555); unit_warn_if_dir_nonempty(UNIT(a), a->where); dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; } xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp()); xsprintf(name, "systemd-"PID_FMT, getpid()); if (mount(name, a->where, "autofs", 0, options) < 0) { r = -errno; goto fail; } mounted = true; p[1] = safe_close(p[1]); if (stat(a->where, &st) < 0) { r = -errno; goto fail; } ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } r = autofs_protocol(dev_autofs_fd, ioctl_fd); if (r < 0) goto fail; r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec); if (r < 0) goto fail; /* Autofs fun fact: * * Unless we close the ioctl fd here, for some weird reason * the direct mount will not receive events from the * kernel. */ r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); if (r < 0) goto fail; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); a->pipe_fd = p[0]; a->dev_id = st.st_dev; automount_set_state(a, AUTOMOUNT_WAITING); return; fail: log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); safe_close_pair(p); if (mounted) { r = repeat_unmount(a->where, MNT_DETACH); if (r < 0) log_error_errno(r, "Failed to unmount, ignoring: %m"); } automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static int busname_peek_message(BusName *n) { struct kdbus_cmd_recv cmd_recv = { .size = sizeof(cmd_recv), .flags = KDBUS_RECV_PEEK, }; struct kdbus_cmd_free cmd_free = { .size = sizeof(cmd_free), }; const char *comm = NULL; struct kdbus_item *d; struct kdbus_msg *k; size_t start, ps, sz, delta; void *p = NULL; pid_t pid = 0; int r; /* Generate a friendly debug log message about which process * caused triggering of this bus name. This simply peeks the * metadata of the first queued message and logs it. */ assert(n); /* Let's shortcut things a bit, if debug logging is turned off * anyway. */ if (log_get_max_level() < LOG_DEBUG) return 0; r = ioctl(n->starter_fd, KDBUS_CMD_RECV, &cmd_recv); if (r < 0) { if (errno == EINTR || errno == EAGAIN) return 0; return log_unit_error_errno(UNIT(n), errno, "Failed to query activation message: %m"); } /* We map as late as possible, and unmap imemdiately after * use. On 32bit address space is scarce and we want to be * able to handle a lot of activator connections at the same * time, and hence shouldn't keep the mmap()s around for * longer than necessary. */ ps = page_size(); start = (cmd_recv.msg.offset / ps) * ps; delta = cmd_recv.msg.offset - start; sz = PAGE_ALIGN(delta + cmd_recv.msg.msg_size); p = mmap(NULL, sz, PROT_READ, MAP_SHARED, n->starter_fd, start); if (p == MAP_FAILED) { r = log_unit_error_errno(UNIT(n), errno, "Failed to map activation message: %m"); goto finish; } k = (struct kdbus_msg *) ((uint8_t *) p + delta); KDBUS_ITEM_FOREACH(d, k, items) { switch (d->type) { case KDBUS_ITEM_PIDS: pid = d->pids.pid; break; case KDBUS_ITEM_PID_COMM: comm = d->str; break; } } if (pid > 0) log_unit_debug(UNIT(n), "Activation triggered by process " PID_FMT " (%s)", pid, strna(comm)); r = 0; finish: if (p) (void) munmap(p, sz); cmd_free.offset = cmd_recv.msg.offset; if (ioctl(n->starter_fd, KDBUS_CMD_FREE, &cmd_free) < 0) log_unit_warning(UNIT(n), "Failed to free peeked message, ignoring: %m"); return r; } static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { BusName *n = userdata; assert(n); assert(fd >= 0); if (n->state != BUSNAME_LISTENING) return 0; log_unit_debug(UNIT(n), "Activation request"); if (revents != EPOLLIN) { log_unit_error(UNIT(n), "Got unexpected poll event (0x%x) on starter fd.", revents); goto fail; } busname_peek_message(n); busname_enter_running(n); return 0; fail: busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); return 0; } static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { BusName *n = BUSNAME(u); BusNameResult f; assert(n); assert(pid >= 0); if (pid != n->control_pid) return; n->control_pid = 0; if (is_clean_exit(code, status, NULL)) f = BUSNAME_SUCCESS; else if (code == CLD_EXITED) f = BUSNAME_FAILURE_EXIT_CODE; else if (code == CLD_KILLED) f = BUSNAME_FAILURE_SIGNAL; else if (code == CLD_DUMPED) f = BUSNAME_FAILURE_CORE_DUMP; else assert_not_reached("Unknown sigchld code"); log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); if (n->result == BUSNAME_SUCCESS) n->result = f; switch (n->state) { case BUSNAME_MAKING: if (f == BUSNAME_SUCCESS) busname_enter_listening(n); else busname_enter_signal(n, BUSNAME_SIGTERM, f); break; case BUSNAME_SIGTERM: case BUSNAME_SIGKILL: busname_enter_dead(n, f); break; default: assert_not_reached("Uh, control process died at wrong time."); } /* Notify clients about changed exit status */ unit_add_to_dbus_queue(u); }