#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
/* Initial delay and the interval for printing status messages about running jobs */
-#define JOBS_IN_PROGRESS_WAIT_SEC 5
-#define JOBS_IN_PROGRESS_PERIOD_SEC 1
+#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
+#define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3)
#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
/* Where clients shall send notification messages to */
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_watch_jobs_in_progress(Manager *m) {
+ usec_t next;
+
assert(m);
if (m->jobs_in_progress_event_source)
return 0;
- return sd_event_add_monotonic(m->event, JOBS_IN_PROGRESS_WAIT_SEC, 0, manager_dispatch_jobs_in_progress, m, &m->jobs_in_progress_event_source);
+ next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
+ return sd_event_add_monotonic(m->event, next, 0, manager_dispatch_jobs_in_progress, m, &m->jobs_in_progress_event_source);
}
#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED_ON)-1) + sizeof(ANSI_HIGHLIGHT_RED_ON)-1 + 2*(sizeof(ANSI_HIGHLIGHT_OFF)-1))
}
}
+void manager_flip_auto_status(Manager *m, bool enable) {
+ if (enable) {
+ if (m->show_status == SHOW_STATUS_AUTO)
+ manager_set_show_status(m, SHOW_STATUS_TEMPORARY);
+ } else {
+ if (m->show_status == SHOW_STATUS_TEMPORARY)
+ manager_set_show_status(m, SHOW_STATUS_AUTO);
+ }
+}
+
static void manager_print_jobs_in_progress(Manager *m) {
_cleanup_free_ char *job_of_n = NULL;
Iterator i;
unsigned counter = 0, print_nr;
char cylon[6 + CYLON_BUFFER_EXTRA + 1];
unsigned cylon_pos;
+ char time[FORMAT_TIMESPAN_MAX], limit[FORMAT_TIMESPAN_MAX] = "no limit";
+ uint64_t x;
assert(m);
+ manager_flip_auto_status(m, true);
+
print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
HASHMAP_FOREACH(j, m->jobs, i)
cylon_pos = 14 - cylon_pos;
draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
+ m->jobs_in_progress_iteration++;
+
if (m->n_running_jobs > 1)
if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
job_of_n = NULL;
- manager_status_printf(m, true, cylon, "%sA %s job is running for %s",
- strempty(job_of_n), job_type_to_string(j->type), unit_description(j->unit));
+ format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC);
+ if (job_get_timeout(j, &x) > 0)
+ format_timespan(limit, sizeof(limit), x - j->begin_usec, 1*USEC_PER_SEC);
+
+ manager_status_printf(m, true, cylon,
+ "%sA %s job is running for %s (%s / %s)",
+ strempty(job_of_n),
+ job_type_to_string(j->type),
+ unit_description(j->unit),
+ time, limit);
- m->jobs_in_progress_iteration++;
}
static int manager_watch_idle_pipe(Manager *m) {
return 0;
}
+static void manager_clean_environment(Manager *m) {
+ assert(m);
+
+ /* Let's remove some environment variables that we
+ * need ourselves to communicate with our clients */
+ strv_env_unset_many(
+ m->environment,
+ "NOTIFY_SOCKET",
+ "MAINPID",
+ "MANAGERPID",
+ "LISTEN_PID",
+ "LISTEN_FDS",
+ "WATCHDOG_PID",
+ "WATCHDOG_USEC",
+ NULL);
+}
+
static int manager_default_environment(Manager *m) {
assert(m);
/* Import locale variables LC_*= from configuration */
locale_setup(&m->environment);
- } else
+ } else {
/* The user manager passes its own environment
* along to its children. */
m->environment = strv_copy(environ);
+ }
if (!m->environment)
return -ENOMEM;
+ manager_clean_environment(m);
strv_sort(m->environment);
return 0;
if (r < 0)
goto fail;
- r = hashmap_ensure_allocated(&m->watch_pids, trivial_hash_func, trivial_compare_func);
- if (r < 0)
- goto fail;
-
r = hashmap_ensure_allocated(&m->watch_bus, string_hash_func, string_compare_func);
if (r < 0)
goto fail;
* necessary to ensure that users cannot get access to busses
* of virtualized users when no UID namespacing is used. */
if (m->running_as == SYSTEMD_SYSTEM)
- mkdir_p_label("/dev/kdbus/ns", 0700);
+ mkdir_p_label("/dev/kdbus/domain", 0700);
#endif
return 0;
hashmap_free(m->units);
hashmap_free(m->jobs);
- hashmap_free(m->watch_pids);
+ hashmap_free(m->watch_pids1);
+ hashmap_free(m->watch_pids2);
hashmap_free(m->watch_bus);
sd_event_source_unref(m->signal_event_source);
return n;
}
+static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n) {
+ _cleanup_strv_free_ char **tags = NULL;
+
+ assert(m);
+ assert(u);
+ assert(buf);
+ assert(n > 0);
+
+ tags = strv_split(buf, "\n\r");
+ if (!tags) {
+ log_oom();
+ return;
+ }
+
+ log_debug_unit(u->id, "Got notification message for unit %s", u->id);
+
+ if (UNIT_VTABLE(u)->notify_message)
+ UNIT_VTABLE(u)->notify_message(u, pid, tags);
+}
+
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
ssize_t n;
.iov_base = buf,
.iov_len = sizeof(buf)-1,
};
+ bool found = false;
union {
struct cmsghdr cmsghdr;
};
struct ucred *ucred;
Unit *u;
- _cleanup_strv_free_ char **tags = NULL;
n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT);
if (n <= 0) {
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
- u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
- if (!u) {
- u = manager_get_unit_by_pid(m, ucred->pid);
- if (!u) {
- log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
- continue;
- }
- }
-
assert((size_t) n < sizeof(buf));
buf[n] = 0;
- tags = strv_split(buf, "\n\r");
- if (!tags)
- return log_oom();
- log_debug_unit(u->id, "Got notification message for unit %s", u->id);
+ u = manager_get_unit_by_pid(m, ucred->pid);
+ if (u) {
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+ found = true;
+ }
+
+ u = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
+ if (u) {
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+ found = true;
+ }
+
+ u = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
+ if (u) {
+ manager_invoke_notify_message(m, u, ucred->pid, buf, n);
+ found = true;
+ }
- if (UNIT_VTABLE(u)->notify_message)
- UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
+ if (!found)
+ log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
}
return 0;
}
+static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
+ assert(m);
+ assert(u);
+ assert(si);
+
+ log_debug_unit(u->id, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
+
+ unit_unwatch_pid(u, si->si_pid);
+ UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
+}
+
static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
siginfo_t si = {};
- Unit *u;
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
_cleanup_free_ char *name = NULL;
+ Unit *u;
get_process_comm(si.si_pid, &name);
- log_debug("Got SIGCHLD for process "PID_FMT" (%s)", si.si_pid, strna(name));
- }
- /* And now figure out the unit this belongs to */
- u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid));
- if (!u)
+ log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
+ si.si_pid, strna(name),
+ sigchld_code_to_string(si.si_code),
+ si.si_status,
+ strna(si.si_code == CLD_EXITED
+ ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
+ : signal_to_string(si.si_status)));
+
+ /* And now figure out the unit this belongs
+ * to, it might be multiple... */
u = manager_get_unit_by_pid(m, si.si_pid);
+ if (u)
+ invoke_sigchld_event(m, u, &si);
+ u = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
+ if (u)
+ invoke_sigchld_event(m, u, &si);
+ u = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
+ if (u)
+ invoke_sigchld_event(m, u, &si);
+ }
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
return -errno;
}
-
- if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
- continue;
-
- log_debug("Child %lu died (code=%s, status=%i/%s)",
- (long unsigned) si.si_pid,
- sigchld_code_to_string(si.si_code),
- si.si_status,
- strna(si.si_code == CLD_EXITED
- ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
- : signal_to_string(si.si_status)));
-
- if (!u)
- continue;
-
- log_debug_unit(u->id,
- "Child %lu belongs to %s", (long unsigned) si.si_pid, u->id);
-
- hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid));
- UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
}
return 0;
return -errno;
}
- if (sfsi.ssi_pid > 0) {
- _cleanup_free_ char *p = NULL;
-
- get_process_comm(sfsi.ssi_pid, &p);
-
- log_full(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && m->running_as == SYSTEMD_USER)
- ? LOG_DEBUG : LOG_INFO,
- "Received SIG%s from PID "PID_FMT" (%s).",
- signal_to_string(sfsi.ssi_signo),
- sfsi.ssi_pid, strna(p));
- } else
- log_full(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && m->running_as == SYSTEMD_USER)
- ? LOG_DEBUG : LOG_INFO,
- "Received SIG%s.",
- signal_to_string(sfsi.ssi_signo));
+ log_received_signal(sfsi.ssi_signo == SIGCHLD ||
+ (sfsi.ssi_signo == SIGTERM && m->running_as == SYSTEMD_USER)
+ ? LOG_DEBUG : LOG_INFO,
+ &sfsi);
switch (sfsi.ssi_signo) {
case 20:
log_debug("Enabling showing of status.");
- manager_set_show_status(m, true);
+ manager_set_show_status(m, SHOW_STATUS_YES);
break;
case 21:
log_debug("Disabling showing of status.");
- manager_set_show_status(m, false);
+ manager_set_show_status(m, SHOW_STATUS_NO);
break;
case 22:
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata) {
Manager *m = userdata;
+ int r;
+ uint64_t next;
assert(m);
+ assert(source);
manager_print_jobs_in_progress(m);
- return 0;
+
+ next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_PERIOD_USEC;
+ r = sd_event_source_set_time(source, next);
+ if (r < 0)
+ return r;
+
+ return sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
}
int manager_loop(Manager *m) {
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
- char *p;
+ _cleanup_free_ char *p = NULL;
int audit_fd;
audit_fd = get_audit_fd();
} else
log_warning("Failed to send audit message: %m");
}
-
- free(p);
#endif
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
- int fd = -1;
- union sockaddr_union sa;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "\0/org/freedesktop/plymouthd",
+ };
+
int n = 0;
- char *message = NULL;
+ _cleanup_free_ char *message = NULL;
+ _cleanup_close_ int fd = -1;
/* Don't generate plymouth events if the service was already
* started and we're just deserializing */
return;
}
- zero(sa);
- sa.sa.sa_family = AF_UNIX;
- strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
- if (errno != EPIPE &&
- errno != EAGAIN &&
- errno != ENOENT &&
- errno != ECONNREFUSED &&
- errno != ECONNRESET &&
- errno != ECONNABORTED)
+ if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error("connect() failed: %m");
-
- goto finish;
+ return;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
log_oom();
- goto finish;
+ return;
}
errno = 0;
- if (write(fd, message, n + 1) != n + 1) {
-
- if (errno != EPIPE &&
- errno != EAGAIN &&
- errno != ENOENT &&
- errno != ECONNREFUSED &&
- errno != ECONNRESET &&
- errno != ECONNABORTED)
+ if (write(fd, message, n + 1) != n + 1)
+ if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error("Failed to write Plymouth message: %m");
-
- goto finish;
- }
-
-finish:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- free(message);
}
void manager_dispatch_bus_name_owner_changed(
}
int manager_open_serialization(Manager *m, FILE **_f) {
- _cleanup_free_ char *path = NULL;
+ const char *path;
int fd = -1;
FILE *f;
assert(_f);
- if (m->running_as == SYSTEMD_SYSTEM)
- asprintf(&path, "/run/systemd/dump-"PID_FMT"-XXXXXX", getpid());
- else
- asprintf(&path, "/tmp/systemd-dump-"PID_FMT"-XXXXXX", getpid());
-
- if (!path)
- return -ENOMEM;
-
- RUN_WITH_UMASK(0077) {
- fd = mkostemp(path, O_RDWR|O_CLOEXEC);
- }
-
+ path = m->running_as == SYSTEMD_SYSTEM ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
- unlink(path);
log_debug("Serializing state to %s", path);
f = fdopen(fd, "w+");
m->jobs_in_progress_event_source = sd_event_source_unref(m->jobs_in_progress_event_source);
if (hashmap_size(m->jobs) > 0) {
- if (m->jobs_in_progress_event_source)
- sd_event_source_set_time(m->jobs_in_progress_event_source, JOBS_IN_PROGRESS_PERIOD_SEC);
+ if (m->jobs_in_progress_event_source) {
+ uint64_t next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
+ sd_event_source_set_time(m->jobs_in_progress_event_source, next);
+ }
return;
}
+ manager_flip_auto_status(m, false);
+
/* Notify Type=idle units that we are done now */
m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
manager_close_idle_pipe(m);
if (b != l)
strv_free(b);
- m->environment = strv_sort(l);
+ m->environment = l;
+ manager_clean_environment(m);
+ strv_sort(m->environment);
+
return 0;
}
log_open();
}
-void manager_set_show_status(Manager *m, bool b) {
+void manager_set_show_status(Manager *m, ShowStatus mode) {
assert(m);
+ assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
if (m->running_as != SYSTEMD_SYSTEM)
return;
- m->show_status = b;
+ m->show_status = mode;
- if (b)
+ if (mode > 0)
touch("/run/systemd/show-status");
else
unlink("/run/systemd/show-status");
if (m->no_console_output)
return false;
- if (m->show_status)
+ if (m->show_status > 0)
return true;
/* If Plymouth is running make sure we show the status, so