X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fmanager.c;h=1ddd8bae62497407e555bbe4cfa47a03f507754e;hp=6fecbc3f7111aafe1464ceb718de3e8d808b4988;hb=8a3a1704c7738a49385e1e1a2a5041f50e79f57e;hpb=c1165f822cd9f8c3467b5f825ce933ab8374b361 diff --git a/src/core/manager.c b/src/core/manager.c index 6fecbc3f7..1ddd8bae6 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef HAVE_AUDIT #include @@ -120,19 +121,66 @@ static int manager_setup_notify(Manager *m) { ev.events = EPOLLIN; ev.data.ptr = &m->notify_watch; - if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); return -errno; + } sa.un.sun_path[0] = '@'; m->notify_socket = strdup(sa.un.sun_path); if (!m->notify_socket) - return -ENOMEM; + return log_oom(); log_debug("Using notification socket %s", m->notify_socket); return 0; } +static int manager_setup_time_change(Manager *m) { + struct epoll_event ev; + struct itimerspec its; + + assert(m); + assert(m->time_change_watch.type == WATCH_INVALID); + + /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever + * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ + + m->time_change_watch.type = WATCH_TIME_CHANGE; + m->time_change_watch.fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (m->time_change_watch.fd < 0) { + log_error("Failed to create timerfd: %m"); + return -errno; + } + + zero(its); + + /* We only care for the cancellation event, hence we set the + * timeout to the latest possible value. */ + assert_cc(sizeof(time_t) == sizeof(long)); + its.it_value.tv_sec = LONG_MAX; + + if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { + log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + return 0; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = &m->time_change_watch; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) { + log_error("Failed to add timer change fd to epoll: %m"); + return -errno; + } + + log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); + + return 0; +} + static int enable_special_signals(Manager *m) { int fd; @@ -199,6 +247,7 @@ static int manager_setup_signals(Manager *m) { SIGRTMIN+21, /* systemd: disable status messages */ SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */ SIGRTMIN+23, /* systemd: set log level to LOG_INFO */ + SIGRTMIN+24, /* systemd: Immediate exit (--user only) */ SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */ SIGRTMIN+27, /* systemd: set log target to console */ SIGRTMIN+28, /* systemd: set log target to kmsg */ @@ -207,7 +256,8 @@ static int manager_setup_signals(Manager *m) { assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); m->signal_watch.type = WATCH_SIGNAL; - if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) + m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (m->signal_watch.fd < 0) return -errno; zero(ev); @@ -258,7 +308,13 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) { m->pin_cgroupfs_fd = -1; m->idle_pipe[0] = m->idle_pipe[1] = -1; - m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1; + watch_init(&m->signal_watch); + watch_init(&m->mount_watch); + watch_init(&m->swap_watch); + watch_init(&m->udev_watch); + watch_init(&m->time_change_watch); + + m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->environment = strv_copy(environ); @@ -288,20 +344,29 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) { if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func))) goto fail; - if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) + m->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (m->epoll_fd < 0) + goto fail; + + r = manager_setup_signals(m); + if (r < 0) goto fail; - if ((r = manager_setup_signals(m)) < 0) + r = manager_setup_cgroup(m); + if (r < 0) goto fail; - if ((r = manager_setup_cgroup(m)) < 0) + r = manager_setup_notify(m); + if (r < 0) goto fail; - if ((r = manager_setup_notify(m)) < 0) + r = manager_setup_time_change(m); + if (r < 0) goto fail; /* Try to connect to the busses, if possible. */ - if ((r = bus_init(m, running_as != SYSTEMD_SYSTEM)) < 0) + r = bus_init(m, running_as != SYSTEMD_SYSTEM); + if (r < 0) goto fail; m->taint_usr = dir_is_empty("/usr") > 0; @@ -486,6 +551,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->signal_watch.fd); if (m->notify_watch.fd >= 0) close_nointr_nofail(m->notify_watch.fd); + if (m->time_change_watch.fd >= 0) + close_nointr_nofail(m->time_change_watch.fd); free(m->notify_socket); @@ -1272,6 +1339,15 @@ static int manager_process_signal_fd(Manager *m) { log_notice("Setting log level to info."); break; + case 24: + if (m->running_as == SYSTEMD_USER) { + m->exit_code = MANAGER_EXIT; + return 0; + } + + /* This is a nop on init */ + break; + case 26: log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_notice("Setting log target to journal-or-kmsg."); @@ -1353,11 +1429,13 @@ static int process_event(Manager *m, struct epoll_event *ev) { ssize_t k; /* Some timer event, to be dispatched to the units */ - if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) { + k = read(w->fd, &v, sizeof(v)); + if (k != sizeof(v)) { if (k < 0 && (errno == EINTR || errno == EAGAIN)) break; + log_error("Failed to read timer event counter: %s", k < 0 ? strerror(-k) : "Short read"); return k < 0 ? -errno : -EIO; } @@ -1391,6 +1469,28 @@ static int process_event(Manager *m, struct epoll_event *ev) { bus_timeout_event(m, w, ev->events); break; + case WATCH_TIME_CHANGE: { + Unit *u; + Iterator i; + + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "MESSAGE=Time has been changed", + NULL); + + /* Restart the watch */ + close_nointr_nofail(m->time_change_watch.fd); + watch_init(&m->time_change_watch); + manager_setup_time_change(m); + + HASHMAP_FOREACH(u, m->units, i) { + if (UNIT_VTABLE(u)->time_change) + UNIT_VTABLE(u)->time_change(u); + } + + break; + } + default: log_error("event type=%i", w->type); assert_not_reached("Unknown epoll event type."); @@ -1522,10 +1622,12 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { if (!startswith(s, "/org/freedesktop/systemd1/job/")) return -EINVAL; - if ((r = safe_atou(s + 30, &id)) < 0) + r = safe_atou(s + 30, &id); + if (r < 0) return r; - if (!(j = manager_get_job(m, id))) + j = manager_get_job(m, id); + if (!j) return -ENOENT; *_j = j; @@ -2039,7 +2141,7 @@ void manager_check_finished(Manager *m) { if (!log_on_console()) log_struct(LOG_INFO, - "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, "INITRD_USEC=%llu", (unsigned long long) initrd_usec, "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, @@ -2055,7 +2157,7 @@ void manager_check_finished(Manager *m) { if (!log_on_console()) log_struct(LOG_INFO, - "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", @@ -2070,7 +2172,7 @@ void manager_check_finished(Manager *m) { if (!log_on_console()) log_struct(LOG_INFO, - "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, "MESSAGE=Startup finished in %s.", format_timespan(sum, sizeof(sum), total_usec), @@ -2297,3 +2399,10 @@ bool manager_get_show_status(Manager *m) { return plymouth_running(); } + +void watch_init(Watch *w) { + assert(w); + + w->type = WATCH_INVALID; + w->fd = -1; +}