#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
+#include <sys/timerfd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
#include "watchdog.h"
#include "cgroup-util.h"
#include "path-util.h"
+#include "audit-fd.h"
/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_ENTRIES_MAX 16
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;
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 */
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);
m->pin_cgroupfs_fd = -1;
m->idle_pipe[0] = m->idle_pipe[1] = -1;
-#ifdef HAVE_AUDIT
- m->audit_fd = -1;
-#endif
+ 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->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
+ 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);
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;
- if ((r = manager_setup_signals(m)) < 0)
+ r = manager_setup_signals(m);
+ if (r < 0)
goto fail;
- if ((r = manager_setup_cgroup(m)) < 0)
+ r = manager_setup_cgroup(m);
+ if (r < 0)
goto fail;
- if ((r = manager_setup_notify(m)) < 0)
+ r = manager_setup_notify(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 = manager_setup_time_change(m);
+ if (r < 0)
goto fail;
-#ifdef HAVE_AUDIT
- if ((m->audit_fd = audit_open()) < 0 &&
- /* If the kernel lacks netlink or audit support,
- * don't worry about it. */
- errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
- log_error("Failed to connect to audit log: %m");
-#endif
+ /* Try to connect to the busses, if possible. */
+ r = bus_init(m, running_as != SYSTEMD_SYSTEM);
+ if (r < 0)
+ goto fail;
m->taint_usr = dir_is_empty("/usr") > 0;
close_nointr_nofail(m->signal_watch.fd);
if (m->notify_watch.fd >= 0)
close_nointr_nofail(m->notify_watch.fd);
-
-#ifdef HAVE_AUDIT
- if (m->audit_fd >= 0)
- audit_close(m->audit_fd);
-#endif
+ if (m->time_change_watch.fd >= 0)
+ close_nointr_nofail(m->time_change_watch.fd);
free(m->notify_socket);
r = q;
}
+ /* Any fds left? Find some unit which wants them. This is
+ * useful to allow container managers to pass some file
+ * descriptors to us pre-initialized. This enables
+ * socket-based activation of entire containers. */
+ if (fdset_size(fds) > 0) {
+ q = manager_distribute_fds(m, fds);
+ if (q < 0)
+ r = q;
+ }
+
/* Third, fire things up! */
q = manager_coldplug(m);
if (q < 0)
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.");
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;
}
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.");
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;
#ifdef HAVE_AUDIT
char *p;
+ int audit_fd;
- if (m->audit_fd < 0)
+ audit_fd = get_audit_fd();
+ if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
return;
}
- if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
+ if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
if (errno == EPERM) {
/* We aren't allowed to send audit messages?
* Then let's not retry again. */
- audit_close(m->audit_fd);
- m->audit_fd = -1;
+ close_audit_fd();
} else
log_warning("Failed to send audit message: %m");
}
log_debug("Serializing state to %s", path);
free(path);
- if (!(f = fdopen(fd, "w+")))
+ f = fdopen(fd, "w+");
+ if (!f)
return -errno;
*_f = f;
if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0)
goto finish;
- if ((r = unit_deserialize(u, f, fds)) < 0)
+ r = unit_deserialize(u, f, fds);
+ if (r < 0)
goto finish;
}
return r;
}
+int manager_distribute_fds(Manager *m, FDSet *fds) {
+ Unit *u;
+ Iterator i;
+ int r;
+
+ assert(m);
+
+ HASHMAP_FOREACH(u, m->units, i) {
+
+ if (fdset_size(fds) <= 0)
+ break;
+
+ if (UNIT_VTABLE(u)->distribute_fds) {
+ r = UNIT_VTABLE(u)->distribute_fds(u, fds);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
int manager_reload(Manager *m) {
int r, q;
FILE *f;
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,
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.",
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),
return plymouth_running();
}
+
+void watch_init(Watch *w) {
+ assert(w);
+
+ w->type = WATCH_INVALID;
+ w->fd = -1;
+}