#include "util.h"
#include "mkdir.h"
#include "ratelimit.h"
-#include "cgroup.h"
+#include "locale-setup.h"
#include "mount-setup.h"
#include "unit-name.h"
#include "dbus-unit.h"
#include "efivars.h"
#include "env-util.h"
-/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
-#define GC_QUEUE_ENTRIES_MAX 16
-
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#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_PERIOD_DIVISOR 3
+
/* Where clients shall send notification messages to */
#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify"
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
- struct epoll_event ev;
- int one = 1;
-
- assert(m);
+ } sa = {
+ .sa.sa_family = AF_UNIX,
+ };
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->notify_watch,
+ };
+ int one = 1, r;
m->notify_watch.type = WATCH_NOTIFY;
m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
return -errno;
}
- zero(sa);
- sa.sa.sa_family = AF_UNIX;
-
if (getpid() != 1 || detect_container(NULL) > 0)
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull());
else
sa.un.sun_path[0] = 0;
- if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
+ r = bind(m->notify_watch.fd, &sa.sa,
+ offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
+ if (r < 0) {
log_error("bind() failed: %m");
return -errno;
}
- if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
+ r = setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (r < 0) {
log_error("SO_PASSCRED failed: %m");
return -errno;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->notify_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) {
+ r = epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev);
+ if (r < 0) {
log_error("Failed to add notification socket fd to epoll: %m");
return -errno;
}
return 0;
}
+static int manager_jobs_in_progress_mod_timer(Manager *m) {
+ struct itimerspec its = {
+ .it_value.tv_sec = JOBS_IN_PROGRESS_WAIT_SEC,
+ .it_interval.tv_sec = JOBS_IN_PROGRESS_PERIOD_SEC,
+ };
+
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return 0;
+
+ if (timerfd_settime(m->jobs_in_progress_watch.fd, 0, &its, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int manager_watch_jobs_in_progress(Manager *m) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->jobs_in_progress_watch,
+ };
+ int r;
+
+ if (m->jobs_in_progress_watch.type != WATCH_INVALID)
+ return 0;
+
+ m->jobs_in_progress_watch.type = WATCH_JOBS_IN_PROGRESS;
+ m->jobs_in_progress_watch.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->jobs_in_progress_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ r = manager_jobs_in_progress_mod_timer(m);
+ if (r < 0) {
+ log_error("Failed to set up timer for jobs progress watch: %s", strerror(-r));
+ goto err;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->jobs_in_progress_watch.fd, &ev) < 0) {
+ log_error("Failed to add jobs progress timer fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up jobs progress timerfd.");
+
+ return 0;
+
+err:
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ return r;
+}
+
+static void manager_unwatch_jobs_in_progress(Manager *m) {
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->jobs_in_progress_watch.fd, NULL) >= 0);
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ m->jobs_in_progress_iteration = 0;
+
+ log_debug("Closed jobs progress timerfd.");
+}
+
+#define CYLON_BUFFER_EXTRA (2*strlen(ANSI_RED_ON) + strlen(ANSI_HIGHLIGHT_RED_ON) + 2*strlen(ANSI_HIGHLIGHT_OFF))
+static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
+ char *p = buffer;
+
+ assert(buflen >= CYLON_BUFFER_EXTRA + width + 1);
+ assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */
+
+ if (pos > 1) {
+ if (pos > 2)
+ p = mempset(p, ' ', pos-2);
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ }
+
+ if (pos > 0 && pos <= width) {
+ p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON);
+ *p++ = '*';
+ }
+
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+
+ if (pos < width) {
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ if (pos < width-1)
+ p = mempset(p, ' ', width-1-pos);
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+ }
+}
+
+static void manager_print_jobs_in_progress(Manager *m) {
+ Iterator i;
+ Job *j;
+ char *job_of_n = NULL;
+ unsigned counter = 0, print_nr;
+ char cylon[6 + CYLON_BUFFER_EXTRA + 1];
+ unsigned cylon_pos;
+
+ print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->state == JOB_RUNNING && counter++ == print_nr)
+ break;
+
+ /* m->n_running_jobs must be consistent with the contents of m->jobs,
+ * so the above loop must have succeeded in finding j. */
+ assert(counter == print_nr + 1);
+
+ cylon_pos = m->jobs_in_progress_iteration % 14;
+ if (cylon_pos >= 8)
+ cylon_pos = 14 - cylon_pos;
+ draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
+
+ 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));
+ free(job_of_n);
+
+ m->jobs_in_progress_iteration++;
+}
+
+static int manager_watch_idle_pipe(Manager *m) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->idle_pipe_watch,
+ };
+ int r;
+
+ if (m->idle_pipe_watch.type != WATCH_INVALID)
+ return 0;
+
+ if (m->idle_pipe[2] < 0)
+ return 0;
+
+ m->idle_pipe_watch.type = WATCH_IDLE_PIPE;
+ m->idle_pipe_watch.fd = m->idle_pipe[2];
+ if (m->idle_pipe_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_pipe_watch.fd, &ev) < 0) {
+ log_error("Failed to add idle_pipe fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up idle_pipe watch.");
+
+ return 0;
+
+err:
+ if (m->idle_pipe_watch.fd >= 0)
+ close_nointr_nofail(m->idle_pipe_watch.fd);
+ watch_init(&m->idle_pipe_watch);
+ return r;
+}
+
+static void manager_unwatch_idle_pipe(Manager *m) {
+ if (m->idle_pipe_watch.type != WATCH_IDLE_PIPE)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->idle_pipe_watch.fd, NULL) >= 0);
+ watch_init(&m->idle_pipe_watch);
+
+ log_debug("Closed idle_pipe watch.");
+}
+
static int manager_setup_time_change(Manager *m) {
- struct epoll_event ev;
- struct itimerspec its;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->time_change_watch,
+ };
+
+ /* We only care for the cancellation event, hence we set the
+ * timeout to the latest possible value. */
+ struct itimerspec its = {
+ .it_value.tv_sec = TIME_T_MAX,
+ };
+ assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
- assert(m);
assert(m->time_change_watch.type == WATCH_INVALID);
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
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(TIME_T_MAX));
- its.it_value.tv_sec = TIME_T_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);
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;
static int manager_setup_signals(Manager *m) {
sigset_t mask;
- struct epoll_event ev;
- struct sigaction sa;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->signal_watch,
+ };
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+ };
assert(m);
/* We are not interested in SIGSTOP and friends. */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
assert_se(sigemptyset(&mask) == 0);
if (m->signal_watch.fd < 0)
return -errno;
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->signal_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
return -errno;
return 0;
}
-static void manager_strip_environment(Manager *m) {
+static int manager_default_environment(Manager *m) {
+#ifdef HAVE_SPLIT_USR
+ const char *path = "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+#else
+ const char *path = "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin";
+#endif
+
assert(m);
- /* Remove variables from the inherited set that are part of
- * the container interface:
- * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
- strv_remove_prefix(m->environment, "container=");
- strv_remove_prefix(m->environment, "container_");
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ /* The system manager always starts with a clean
+ * environment for its children. It does not import
+ * the kernel or the parents exported variables.
+ *
+ * The initial passed environ is untouched to keep
+ * /proc/self/environ valid; it is used for tagging
+ * the init process inside containers. */
+ m->environment = strv_new(path, NULL);
+
+ /* Import locale variables LC_*= from configuration */
+ locale_setup(&m->environment);
+ } else
+ /* The user manager passes its own environment
+ * along to its children. */
+ m->environment = strv_copy(environ);
- /* Remove variables from the inherited set that are part of
- * the initrd interface:
- * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
- strv_remove_prefix(m->environment, "RD_");
+ if (!m->environment)
+ return -ENOMEM;
- /* Drop invalid entries */
- strv_env_clean(m->environment);
+ return 0;
}
-int manager_new(SystemdRunningAs running_as, Manager **_m) {
+int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
Manager *m;
int r = -ENOMEM;
if (!m)
return -ENOMEM;
- dual_timestamp_get(&m->userspace_timestamp);
- dual_timestamp_from_monotonic(&m->kernel_timestamp, 0);
- efi_get_boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
+#ifdef ENABLE_EFI
+ if (detect_container(NULL) <= 0)
+ efi_get_boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
+#endif
m->running_as = running_as;
m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->pin_cgroupfs_fd = -1;
- m->idle_pipe[0] = m->idle_pipe[1] = -1;
+ m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -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);
+ watch_init(&m->jobs_in_progress_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);
- if (!m->environment)
+ r = manager_default_environment(m);
+ if (r < 0)
goto fail;
- manager_strip_environment(m);
-
- if (running_as == SYSTEMD_SYSTEM) {
- m->default_controllers = strv_new("cpu", NULL);
- if (!m->default_controllers)
- goto fail;
- }
-
if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
goto fail;
if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
goto fail;
- if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
+ m->cgroup_unit = hashmap_new(string_hash_func, string_compare_func);
+ if (!m->cgroup_unit)
goto fail;
- if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
+ m->watch_bus = hashmap_new(string_hash_func, string_compare_func);
+ if (!m->watch_bus)
goto fail;
m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
goto fail;
/* Try to connect to the busses, if possible. */
- r = bus_init(m, running_as != SYSTEMD_SYSTEM);
- if (r < 0)
- goto fail;
+ if ((running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS")) ||
+ running_as == SYSTEMD_SYSTEM) {
+ r = bus_init(m, reexecuting || running_as != SYSTEMD_SYSTEM);
+ if (r < 0)
+ goto fail;
+ } else
+ log_debug("Skipping DBus session bus connection attempt - no DBUS_SESSION_BUS_ADDRESS set...");
m->taint_usr = dir_is_empty("/usr") > 0;
assert(m);
- if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) &&
- (m->gc_queue_timestamp <= 0 ||
- (m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC)))
- return 0;
-
- log_debug("Running GC...");
+ /* log_debug("Running GC..."); */
m->gc_marker += _GC_OFFSET_MAX;
if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
}
m->n_in_gc_queue = 0;
- m->gc_queue_timestamp = 0;
return n;
}
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
+
+ m->n_on_console = 0;
+ m->n_running_jobs = 0;
+}
+
+static void close_idle_pipe(Manager *m) {
+ close_pipe(m->idle_pipe);
+ close_pipe(m->idle_pipe + 2);
}
void manager_free(Manager *m) {
close_nointr_nofail(m->notify_watch.fd);
if (m->time_change_watch.fd >= 0)
close_nointr_nofail(m->time_change_watch.fd);
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
free(m->notify_socket);
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
- strv_free(m->default_controllers);
-
- hashmap_free(m->cgroup_bondings);
+ hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
- close_pipe(m->idle_pipe);
+ close_idle_pipe(m);
free(m->switch_root);
free(m->switch_root_init);
static void manager_build_unit_path_cache(Manager *m) {
char **i;
- DIR _cleanup_free_ *d = NULL;
+ _cleanup_free_ DIR *d = NULL;
int r;
assert(m);
goto fail;
}
- r = set_put(m->unit_path_cache, p);
- if (r < 0) {
- free(p);
+ r = set_consume(m->unit_path_cache, p);
+ if (r < 0)
goto fail;
- }
}
closedir(d);
assert(m);
+ dual_timestamp_get(&m->generators_start_timestamp);
manager_run_generators(m);
+ dual_timestamp_get(&m->generators_finish_timestamp);
r = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
m->n_reloading ++;
/* First, enumerate what we can from all config files */
+ dual_timestamp_get(&m->unitsload_start_timestamp);
r = manager_enumerate(m);
+ dual_timestamp_get(&m->unitsload_finish_timestamp);
/* Second, deserialize if there is something to deserialize */
if (serialization) {
if (serialization) {
assert(m->n_reloading > 0);
m->n_reloading --;
+
+ /* Let's wait for the UnitNew/JobNew messages being
+ * sent, before we notify that the reload is
+ * finished */
+ m->send_reloading_done = true;
}
return r;
job_type_collapse(&type, unit);
- tr = transaction_new();
+ tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
return -ENOMEM;
return n;
}
-int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
+int manager_load_unit_prepare(
+ Manager *m,
+ const char *name,
+ const char *path,
+ DBusError *e,
+ Unit **_ret) {
+
Unit *ret;
UnitType t;
int r;
}
}
- if ((r = unit_add_name(ret, name)) < 0) {
+ r = unit_add_name(ret, name);
+ if (r < 0) {
unit_free(ret);
return r;
}
return 0;
}
-int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
+int manager_load_unit(
+ Manager *m,
+ const char *name,
+ const char *path,
+ DBusError *e,
+ Unit **_ret) {
+
int r;
assert(m);
}
m->dispatching_run_queue = false;
+
+ if (m->n_running_jobs > 0)
+ manager_watch_jobs_in_progress(m);
+
+ if (m->n_on_console > 0)
+ manager_watch_idle_pipe(m);
+
return n;
}
}
m->dispatching_dbus_queue = false;
+
+ if (m->send_reloading_done) {
+ m->send_reloading_done = false;
+
+ bus_broadcast_reloading(m, false);
+ }
+
return n;
}
for (;;) {
char buf[4096];
- struct msghdr msghdr;
- struct iovec iovec;
- struct ucred *ucred;
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control;
+ } control = {};
+
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct ucred *ucred;
Unit *u;
- char **tags;
-
- zero(iovec);
- iovec.iov_base = buf;
- iovec.iov_len = sizeof(buf)-1;
-
- zero(control);
- zero(msghdr);
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_control = &control;
- msghdr.msg_controllen = sizeof(control);
+ _cleanup_strv_free_ char **tags = NULL;
n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT);
if (n <= 0) {
- if (n >= 0)
+ if (n == 0)
return -EIO;
if (errno == EAGAIN || errno == EINTR)
u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
if (!u) {
- u = cgroup_unit_by_pid(m, ucred->pid);
+ u = manager_get_unit_by_pid(m, ucred->pid);
if (!u) {
log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
continue;
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
-
- strv_free(tags);
}
return 0;
assert(m);
for (;;) {
- siginfo_t si;
+ siginfo_t si = {};
Unit *u;
int r;
- zero(si);
-
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
* it while it is a zombie. */
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
- char _cleanup_free_ *name = NULL;
+ _cleanup_free_ char *name = NULL;
get_process_comm(si.si_pid, &name);
log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) 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)
- u = cgroup_unit_by_pid(m, si.si_pid);
+ u = manager_get_unit_by_pid(m, si.si_pid);
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
case SIGINT:
if (m->running_as == SYSTEMD_SYSTEM) {
- manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
+ manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
break;
}
NULL);
/* Restart the watch */
+ epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->time_change_watch.fd,
+ NULL);
close_nointr_nofail(m->time_change_watch.fd);
watch_init(&m->time_change_watch);
manager_setup_time_change(m);
break;
}
+ case WATCH_JOBS_IN_PROGRESS: {
+ uint64_t v;
+
+ /* not interested in the data */
+ read(w->fd, &v, sizeof(v));
+
+ manager_print_jobs_in_progress(m);
+ break;
+ }
+
+ case WATCH_IDLE_PIPE: {
+ m->no_console_output = true;
+
+ manager_unwatch_idle_pipe(m);
+ close_idle_pipe(m);
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
if (manager_dispatch_load_queue(m) > 0)
continue;
- if (manager_dispatch_run_queue(m) > 0)
+ if (manager_dispatch_gc_queue(m) > 0)
continue;
- if (bus_dispatch(m) > 0)
+ if (manager_dispatch_cleanup_queue(m) > 0)
continue;
- if (manager_dispatch_cleanup_queue(m) > 0)
+ if (manager_dispatch_cgroup_queue(m) > 0)
continue;
- if (manager_dispatch_gc_queue(m) > 0)
+ if (manager_dispatch_run_queue(m) > 0)
+ continue;
+
+ if (bus_dispatch(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u) {
- char *n;
+ _cleanup_free_ char *n = NULL;
Unit *u;
int r;
assert(s);
assert(_u);
- if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
- return -EINVAL;
-
- n = bus_path_unescape(s+31);
- if (!n)
- return -ENOMEM;
+ r = unit_name_from_dbus_path(s, &n);
+ if (r < 0)
+ return r;
r = manager_load_unit(m, n, NULL, e, &u);
- free(n);
-
if (r < 0)
return r;
/* We set SOCK_NONBLOCK here so that we rather drop the
* message then wait for plymouth */
- if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
log_error("socket() failed: %m");
return;
}
int manager_open_serialization(Manager *m, FILE **_f) {
char *path = NULL;
- mode_t saved_umask;
int fd;
FILE *f;
if (!path)
return -ENOMEM;
- saved_umask = umask(0077);
- fd = mkostemp(path, O_RDWR|O_CLOEXEC);
- umask(saved_umask);
+ RUN_WITH_UMASK(0077) {
+ fd = mkostemp(path, O_RDWR|O_CLOEXEC);
+ }
if (fd < 0) {
free(path);
return 0;
}
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
Iterator i;
Unit *u;
const char *t;
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
}
- STRV_FOREACH(e, m->environment) {
- _cleanup_free_ char *ce;
+ if (!switching_root) {
+ STRV_FOREACH(e, m->environment) {
+ _cleanup_free_ char *ce;
- ce = cescape(*e);
- if (ce)
- fprintf(f, "env=%s\n", *e);
+ ce = cescape(*e);
+ if (ce)
+ fprintf(f, "env=%s\n", *e);
+ }
}
+ bus_serialize(m, f);
+
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
fputs(u->id, f);
fputc('\n', f);
- if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) {
+ r = unit_serialize(u, f, fds, !switching_root);
+ if (r < 0) {
m->n_reloading --;
return r;
}
strv_free(m->environment);
m->environment = e;
- } else
+ } else if (bus_deserialize_item(m, l) == 0)
log_debug("Unknown serialization item '%s'", l);
}
return r;
m->n_reloading ++;
+ bus_broadcast_reloading(m, true);
fds = fdset_new();
if (!fds) {
goto finish;
}
- r = manager_serialize(m, f, fds, true);
+ r = manager_serialize(m, f, fds, false);
if (r < 0) {
m->n_reloading --;
goto finish;
assert(m->n_reloading > 0);
m->n_reloading--;
+ m->send_reloading_done = true;
+
finish:
if (f)
fclose(f);
return r;
}
-bool manager_is_booting_or_shutting_down(Manager *m) {
+static bool manager_is_booting_or_shutting_down(Manager *m) {
Unit *u;
assert(m);
return false;
}
+bool manager_is_reloading_or_reexecuting(Manager *m) {
+ assert(m);
+
+ return m->n_reloading != 0;
+}
+
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
unit_reset_failed(u);
}
-bool manager_unit_pending_inactive(Manager *m, const char *name) {
+bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
Unit *u;
assert(m);
if (!u)
return true;
- return unit_pending_inactive(u);
+ return unit_inactive_or_pending(u);
}
void manager_check_finished(Manager *m) {
assert(m);
- if (hashmap_size(m->jobs) > 0)
+ if (m->n_running_jobs == 0)
+ manager_unwatch_jobs_in_progress(m);
+
+ if (hashmap_size(m->jobs) > 0) {
+ manager_jobs_in_progress_mod_timer(m);
return;
+ }
/* Notify Type=idle units that we are done now */
- close_pipe(m->idle_pipe);
+ manager_unwatch_idle_pipe(m);
+ close_idle_pipe(m);
/* Turn off confirm spawn now */
m->confirm_spawn = false;
"INITRD_USEC=%llu", (unsigned long long) initrd_usec,
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(initrd, sizeof(initrd), initrd_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
} else {
kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
"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.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
} else {
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),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
sd_notifyf(false,
"READY=1\nSTATUS=Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec));
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
return log_oom();
if (!mkdtemp(p)) {
- free(p);
log_error("Failed to create generator directory %s: %m",
p);
+ free(p);
return -errno;
}
}
DIR *d = NULL;
const char *generator_path;
const char *argv[5];
- mode_t u;
int r;
assert(m);
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
- u = umask(0022);
- execute_directory(generator_path, d, (char**) argv);
- umask(u);
+ RUN_WITH_UMASK(0022) {
+ execute_directory(generator_path, d, (char**) argv);
+ }
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
remove_generator_dir(m, &m->generator_unit_path_late);
}
-int manager_set_default_controllers(Manager *m, char **controllers) {
- char **l;
+int manager_environment_add(Manager *m, char **environment) {
+ char **e = NULL;
assert(m);
-
- l = strv_copy(controllers);
- if (!l)
+ e = strv_env_merge(2, m->environment, environment);
+ if (!e)
return -ENOMEM;
-
- strv_free(m->default_controllers);
- m->default_controllers = l;
-
- cg_shorten_controllers(m->default_controllers);
-
+ strv_free(m->environment);
+ m->environment = e;
return 0;
}
unlink("/run/systemd/show-status");
}
-bool manager_get_show_status(Manager *m) {
+static bool manager_get_show_status(Manager *m) {
assert(m);
if (m->running_as != SYSTEMD_SYSTEM)
return false;
+ if (m->no_console_output)
+ return false;
+
if (m->show_status)
return true;
return plymouth_running();
}
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) {
+ va_list ap;
+
+ if (!manager_get_show_status(m))
+ return;
+
+ /* XXX We should totally drop the check for ephemeral here
+ * and thus effectively make 'Type=idle' pointless. */
+ if (ephemeral && m->n_on_console > 0)
+ return;
+
+ if (!manager_is_booting_or_shutting_down(m))
+ return;
+
+ va_start(ap, format);
+ status_vprintf(status, true, ephemeral, format, ap);
+ va_end(ap);
+}
+
void watch_init(Watch *w) {
assert(w);