static int automount_coldplug(Unit *u) { Automount *a = AUTOMOUNT(u); int r; assert(a); assert(a->state == AUTOMOUNT_DEAD); if (a->deserialized_state != a->state) { r = open_dev_autofs(u->manager); if (r < 0) return r; if (a->deserialized_state == AUTOMOUNT_WAITING || a->deserialized_state == AUTOMOUNT_RUNNING) { assert(a->pipe_fd >= 0); r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); if (r < 0) return r; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); if (a->deserialized_state == AUTOMOUNT_RUNNING) { r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); } } automount_set_state(a, a->deserialized_state); } return 0; }
static int scope_start(Unit *u) { Scope *s = SCOPE(u); int r; assert(s); if (s->state == SCOPE_FAILED) return -EPERM; /* We can't fulfill this right now, please try again later */ if (s->state == SCOPE_STOP_SIGTERM || s->state == SCOPE_STOP_SIGKILL) return -EAGAIN; assert(s->state == SCOPE_DEAD); if (!u->transient && UNIT(s)->manager->n_reloading <= 0) return -ENOENT; (void) unit_realize_cgroup(u); (void) unit_reset_cpu_usage(u); r = unit_attach_pids_to_cgroup(u); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); return r; } s->result = SCOPE_SUCCESS; scope_set_state(s, SCOPE_RUNNING); return 1; }
static int busname_watch_fd(BusName *n) { int r; assert(n); if (n->starter_fd < 0) return 0; if (n->starter_event_source) { r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON); if (r < 0) goto fail; } else { r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); if (r < 0) goto fail; (void) sd_event_source_set_description(n->starter_event_source, "busname-starter"); } return 0; fail: log_unit_warning_errno(UNIT(n), r, "Failed to watch starter fd: %m"); busname_unwatch_fd(n); return r; }
static void automount_enter_running(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Unit *trigger; struct stat st; int r; assert(a); /* If the user masked our unit in the meantime, fail */ if (UNIT(a)->load_state != UNIT_LOADED) { log_unit_error(UNIT(a), "Suppressing automount event since unit is no longer loaded."); goto fail; } /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); automount_send_ready(a, a->tokens, -EHOSTDOWN); automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* 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; } /* The mount unit may have been explicitly started before we got the * autofs request. Ack it to unblock anything waiting on the mount point. */ if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) { log_unit_info(UNIT(a), "Automount point already active?"); automount_send_ready(a, a->tokens, 0); return; } 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_START, trigger, JOB_REPLACE, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { bool skip_signal = false; int r; assert(s); if (s->result == SCOPE_SUCCESS) s->result = f; /* Before sending any signal, make sure we track all members of this cgroup */ (void) unit_watch_all_pids(UNIT(s)); /* Also, enqueue a job that we recheck all our PIDs a bit later, given that it's likely some processes have * died now */ (void) unit_enqueue_rewatch_pids(UNIT(s)); /* If we have a controller set let's ask the controller nicely to terminate the scope, instead of us going * directly into SIGTERM berserk mode */ if (state == SCOPE_STOP_SIGTERM) skip_signal = bus_scope_send_request_stop(s) > 0; if (skip_signal) r = 1; /* wait */ else { r = unit_kill_context( UNIT(s), &s->kill_context, state != SCOPE_STOP_SIGTERM ? KILL_KILL : s->was_abandoned ? KILL_TERMINATE_AND_LOG : KILL_TERMINATE, -1, -1, false); if (r < 0) goto fail; } if (r > 0) { r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); if (r < 0) goto fail; scope_set_state(s, state); } else if (state == SCOPE_STOP_SIGTERM) scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS); else scope_enter_dead(s, SCOPE_SUCCESS); return; fail: log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); }
static void busname_enter_making(BusName *n) { int r; assert(n); r = busname_open_fd(n); if (r < 0) goto fail; if (n->policy) { /* If there is a policy, we need to resolve user/group * names, which we can't do from PID1, hence let's * fork. */ busname_unwatch_control_pid(n); r = busname_make_starter(n, &n->control_pid); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "%s failed to fork 'making' task: %m", UNIT(n)->id); goto fail; } busname_set_state(n, BUSNAME_MAKING); } else { /* If there is no policy, we can do everything * directly from PID 1, hence do so. */ r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "%s failed to make starter: %m", UNIT(n)->id); goto fail; } busname_enter_listening(n); } return; fail: busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); }
static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f) { KillContext kill_context = {}; int r; assert(n); if (f != BUSNAME_SUCCESS) n->result = f; kill_context_init(&kill_context); r = unit_kill_context(UNIT(n), &kill_context, state != BUSNAME_SIGTERM ? KILL_KILL : KILL_TERMINATE, -1, n->control_pid, false); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "%s failed to kill control process: %m", UNIT(n)->id); goto fail; } if (r > 0) { r = busname_arm_timer(n); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "%s failed to arm timer: %m", UNIT(n)->id); goto fail; } busname_set_state(n, state); } else if (state == BUSNAME_SIGTERM) busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_SUCCESS); else busname_enter_dead(n, BUSNAME_SUCCESS); return; fail: busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); }
int automount_update_mount(Automount *a, MountState old_state, MountState state) { int r; assert(a); switch (state) { case MOUNT_MOUNTED: case MOUNT_REMOUNTING: automount_send_ready(a, a->tokens, 0); r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); break; case MOUNT_DEAD: case MOUNT_UNMOUNTING: case MOUNT_MOUNTING_SIGTERM: case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGTERM: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGTERM: case MOUNT_UNMOUNTING_SIGKILL: case MOUNT_FAILED: if (old_state != state) automount_send_ready(a, a->tokens, -ENODEV); (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); break; default: break; } switch (state) { case MOUNT_DEAD: automount_send_ready(a, a->expire_tokens, 0); break; case MOUNT_MOUNTING: case MOUNT_MOUNTING_DONE: case MOUNT_MOUNTING_SIGTERM: case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGTERM: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGTERM: case MOUNT_UNMOUNTING_SIGKILL: case MOUNT_FAILED: if (old_state != state) automount_send_ready(a, a->expire_tokens, -ENODEV); break; default: break; } return 0; }
static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { bool skip_signal = false; int r; assert(s); if (s->result == SCOPE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); /* If we have a controller set let's ask the controller nicely * to terminate the scope, instead of us going directly into * SIGTERM berserk mode */ if (state == SCOPE_STOP_SIGTERM) skip_signal = bus_scope_send_request_stop(s) > 0; if (skip_signal) r = 1; /* wait */ else { r = unit_kill_context( UNIT(s), &s->kill_context, state != SCOPE_STOP_SIGTERM ? KILL_KILL : s->was_abandoned ? KILL_TERMINATE_AND_LOG : KILL_TERMINATE, -1, -1, false); if (r < 0) goto fail; } if (r > 0) { r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec)); if (r < 0) goto fail; scope_set_state(s, state); } else if (state == SCOPE_STOP_SIGTERM) scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_SUCCESS); else scope_enter_dead(s, SCOPE_SUCCESS); return; fail: log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m"); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); }
static int scope_start(Unit *u) { Scope *s = SCOPE(u); int r; assert(s); if (unit_has_name(u, SPECIAL_INIT_SCOPE)) return -EPERM; if (s->state == SCOPE_FAILED) return -EPERM; /* We can't fulfill this right now, please try again later */ if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) return -EAGAIN; assert(s->state == SCOPE_DEAD); if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) return -ENOENT; (void) bus_scope_track_controller(s); r = unit_acquire_invocation_id(u); if (r < 0) return r; (void) unit_realize_cgroup(u); (void) unit_reset_cpu_accounting(u); (void) unit_reset_ip_accounting(u); unit_export_state_files(UNIT(s)); r = unit_attach_pids_to_cgroup(u, UNIT(s)->pids, NULL); if (r < 0) { log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); return r; } s->result = SCOPE_SUCCESS; scope_set_state(s, SCOPE_RUNNING); /* Start watching the PIDs currently in the scope */ (void) unit_enqueue_rewatch_pids(UNIT(s)); return 1; }
static int busname_watch_fd(BusName *n) { int r; assert(n); if (n->starter_fd < 0) return 0; if (n->starter_event_source) r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_ON); else r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "Failed to watch starter fd: %m"); busname_unwatch_fd(n); return r; } return 0; }
static void automount_enter_runnning(Automount *a) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; int r; assert(a); /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); automount_send_ready(a, a->tokens, -EHOSTDOWN); automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* 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 active?"); else { r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; } } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static void busname_enter_listening(BusName *n) { int r; assert(n); if (n->activating) { r = busname_watch_fd(n); if (r < 0) { log_unit_warning_errno(UNIT(n)->id, r, "%s failed to watch names: %m", UNIT(n)->id); goto fail; } busname_set_state(n, BUSNAME_LISTENING); } else busname_set_state(n, BUSNAME_REGISTERED); return; fail: busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_RESOURCES); }
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 void automount_trigger_notify(Unit *u, Unit *other) { Automount *a = AUTOMOUNT(u); int r; assert(a); assert(other); /* Filter out invocations with bogus state */ if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) return; /* Don't propagate state changes from the mount if we are already down */ if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) return; /* Propagate start limit hit state */ if (other->start_limit_hit) { automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); return; } /* Don't propagate anything if there's still a job queued */ if (other->job) return; /* The mount is successfully established */ if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { (void) automount_send_ready(a, a->tokens, 0); r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); automount_set_state(a, AUTOMOUNT_RUNNING); } if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTING, MOUNT_MOUNTING_DONE, MOUNT_MOUNTED, MOUNT_REMOUNTING, MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILED)) { (void) automount_send_ready(a, a->expire_tokens, -ENODEV); } if (MOUNT(other)->state == MOUNT_DEAD) (void) automount_send_ready(a, a->expire_tokens, 0); /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ if (IN_SET(MOUNT(other)->state, MOUNT_DEAD, MOUNT_UNMOUNTING, MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILED)) { (void) automount_send_ready(a, a->tokens, -ENODEV); automount_set_state(a, AUTOMOUNT_WAITING); } }