Systemd/src/core/manager.c

3122 lines
100 KiB
C
Raw Normal View History

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2009-11-18 00:42:52 +01:00
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <dirent.h>
2009-11-18 00:42:52 +01:00
#include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
2010-01-24 00:39:29 +01:00
#include <signal.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
#include <unistd.h>
2010-08-11 15:19:50 +02:00
#ifdef HAVE_AUDIT
#include <libaudit.h>
2010-08-11 15:19:50 +02:00
#endif
2009-11-18 00:42:52 +01:00
#include "sd-daemon.h"
#include "sd-messages.h"
#include "alloc-util.h"
#include "audit-fd.h"
#include "boot-timestamps.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-util.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
#include "dbus.h"
#include "env-util.h"
#include "escape.h"
#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
2009-11-18 00:42:52 +01:00
#include "hashmap.h"
#include "io-util.h"
#include "locale-setup.h"
#include "log.h"
#include "macro.h"
#include "manager.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
#include "process-util.h"
#include "ratelimit.h"
#include "rm-rf.h"
#include "signal-util.h"
2010-06-18 04:22:59 +02:00
#include "special.h"
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "time-util.h"
#include "transaction.h"
#include "umask-util.h"
#include "unit-name.h"
#include "util.h"
2011-09-23 17:20:45 +02:00
#include "virt.h"
#include "watchdog.h"
2009-11-18 00:42:52 +01:00
#define NOTIFY_RCVBUF_SIZE (8*1024*1024)
/* Initial delay and the interval for printing status messages about running jobs */
#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
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m);
static void manager_undo_generators(Manager *m);
static void manager_watch_jobs_in_progress(Manager *m) {
usec_t next;
int r;
assert(m);
if (m->jobs_in_progress_event_source)
return;
next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
r = sd_event_add_time(
m->event,
&m->jobs_in_progress_event_source,
CLOCK_MONOTONIC,
next, 0,
manager_dispatch_jobs_in_progress, m);
if (r < 0)
return;
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress");
}
#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1))
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);
*p++ = '*';
}
if (pos > 0 && pos <= width) {
p = stpcpy(p, ANSI_HIGHLIGHT_RED);
*p++ = '*';
}
p = stpcpy(p, ANSI_NORMAL);
if (pos < width) {
p = stpcpy(p, ANSI_RED);
*p++ = '*';
if (pos < width-1)
p = mempset(p, ' ', width-1-pos);
strcpy(p, ANSI_NORMAL);
}
}
void manager_flip_auto_status(Manager *m, bool enable) {
assert(m);
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;
Job *j;
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);
assert(m->n_running_jobs > 0);
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)
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);
2013-10-12 01:33:48 +02:00
assert(j);
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);
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;
}
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, STATUS_TYPE_EPHEMERAL, 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);
}
static int have_ask_password(void) {
_cleanup_closedir_ DIR *dir;
dir = opendir("/run/systemd/ask-password");
if (!dir) {
if (errno == ENOENT)
return false;
else
return -errno;
}
for (;;) {
struct dirent *de;
errno = 0;
de = readdir(dir);
if (!de && errno != 0)
return -errno;
if (!de)
return false;
if (startswith(de->d_name, "ask."))
return true;
}
}
static int manager_dispatch_ask_password_fd(sd_event_source *source,
int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
assert(m);
flush_fd(fd);
m->have_ask_password = have_ask_password();
if (m->have_ask_password < 0)
/* Log error but continue. Negative have_ask_password
* is treated as unknown status. */
log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m");
return 0;
}
static void manager_close_ask_password(Manager *m) {
assert(m);
m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source);
m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->have_ask_password = -EINVAL;
}
static int manager_check_ask_password(Manager *m) {
int r;
assert(m);
if (!m->ask_password_event_source) {
assert(m->ask_password_inotify_fd < 0);
mkdir_p_label("/run/systemd/ask-password", 0755);
m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (m->ask_password_inotify_fd < 0)
return log_error_errno(errno, "inotify_init1() failed: %m");
if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) {
log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m");
manager_close_ask_password(m);
return -errno;
}
r = sd_event_add_io(m->event, &m->ask_password_event_source,
m->ask_password_inotify_fd, EPOLLIN,
manager_dispatch_ask_password_fd, m);
if (r < 0) {
log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m");
manager_close_ask_password(m);
return -errno;
}
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password");
/* Queries might have been added meanwhile... */
manager_dispatch_ask_password_fd(m->ask_password_event_source,
m->ask_password_inotify_fd, EPOLLIN, m);
}
return m->have_ask_password;
}
static int manager_watch_idle_pipe(Manager *m) {
int r;
assert(m);
if (m->idle_pipe_event_source)
return 0;
if (m->idle_pipe[2] < 0)
return 0;
r = sd_event_add_io(m->event, &m->idle_pipe_event_source, m->idle_pipe[2], EPOLLIN, manager_dispatch_idle_pipe_fd, m);
if (r < 0)
return log_error_errno(r, "Failed to watch idle pipe: %m");
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->idle_pipe_event_source, "manager-idle-pipe");
return 0;
}
static void manager_close_idle_pipe(Manager *m) {
assert(m);
m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
safe_close_pair(m->idle_pipe);
safe_close_pair(m->idle_pipe + 2);
}
static int manager_setup_time_change(Manager *m) {
int r;
/* 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(m);
assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
if (m->test_run)
return 0;
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
* CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */
m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
if (m->time_change_fd < 0)
return log_error_errno(errno, "Failed to create timerfd: %m");
if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) {
log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m");
m->time_change_fd = safe_close(m->time_change_fd);
return 0;
}
r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m);
if (r < 0)
return log_error_errno(r, "Failed to create time change event source: %m");
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change");
log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
return 0;
}
static int enable_special_signals(Manager *m) {
_cleanup_close_ int fd = -1;
assert(m);
/* Enable that we get SIGINT on control-alt-del. In containers
* this will fail with EPERM (older) or EINVAL (newer), so
* ignore that. */
if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL)
log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m");
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0) {
/* Support systems without virtual console */
if (fd != -ENOENT)
log_warning_errno(errno, "Failed to open /dev/tty0: %m");
} else {
/* Enable that we get SIGWINCH on kbrequest */
if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
log_warning_errno(errno, "Failed to enable kbrequest handling: %m");
}
return 0;
}
2010-01-28 02:01:15 +01:00
static int manager_setup_signals(Manager *m) {
struct sigaction sa = {
.sa_handler = SIG_DFL,
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
};
sigset_t mask;
int r;
2009-11-18 00:42:52 +01:00
2010-01-28 02:01:15 +01:00
assert(m);
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
/* We make liberal use of realtime signals here. On
* Linux/glibc we have 30 of them (with the exception of Linux
* on hppa, see below), between SIGRTMIN+0 ... SIGRTMIN+30
* (aka SIGRTMAX). */
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask,
SIGCHLD, /* Child died */
SIGTERM, /* Reexecute daemon */
SIGHUP, /* Reload configuration */
SIGUSR1, /* systemd/upstart: reconnect to D-Bus */
SIGUSR2, /* systemd: dump status */
SIGINT, /* Kernel sends us this on control-alt-del */
SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */
SIGPWR, /* Some kernel drivers and upsd send us this on power failure */
SIGRTMIN+0, /* systemd: start default.target */
SIGRTMIN+1, /* systemd: isolate rescue.target */
SIGRTMIN+2, /* systemd: isolate emergency.target */
SIGRTMIN+3, /* systemd: start halt.target */
SIGRTMIN+4, /* systemd: start poweroff.target */
SIGRTMIN+5, /* systemd: start reboot.target */
SIGRTMIN+6, /* systemd: start kexec.target */
/* ... space for more special targets ... */
SIGRTMIN+13, /* systemd: Immediate halt */
SIGRTMIN+14, /* systemd: Immediate poweroff */
SIGRTMIN+15, /* systemd: Immediate reboot */
SIGRTMIN+16, /* systemd: Immediate kexec */
/* ... space for more immediate system state changes ... */
SIGRTMIN+20, /* systemd: enable status messages */
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) */
/* .. one free signal here ... */
#if !defined(__hppa64__) && !defined(__hppa__)
/* Apparently Linux on hppa has fewer RT
* signals (SIGRTMAX is SIGRTMIN+25 there),
* hence let's not try to make use of them
* here. Since these commands are accessible
* by different means and only really a safety
* net, the missing functionality on hppa
* shouldn't matter. */
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 */
SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */
/* ... one free signal here SIGRTMIN+30 ... */
#endif
-1);
2010-01-28 02:01:15 +01:00
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (m->signal_fd < 0)
2010-01-28 02:01:15 +01:00
return -errno;
r = sd_event_add_io(m->event, &m->signal_event_source, m->signal_fd, EPOLLIN, manager_dispatch_signal_fd, m);
if (r < 0)
return r;
2010-01-28 02:01:15 +01:00
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->signal_event_source, "manager-signal");
/* Process signals a bit earlier than the rest of things, but
* later than notify_fd processing, so that the notify
* processing can still figure out to which process/service a
* message belongs, before we reap the process. */
r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5);
if (r < 0)
return r;
if (m->running_as == MANAGER_SYSTEM)
return enable_special_signals(m);
2010-01-28 02:01:15 +01:00
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",
"LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
NULL);
}
static int manager_default_environment(Manager *m) {
assert(m);
if (m->running_as == MANAGER_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=" DEFAULT_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);
}
if (!m->environment)
return -ENOMEM;
manager_clean_environment(m);
strv_sort(m->environment);
return 0;
}
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_SYSTEM] = "UNIT=",
[MANAGER_USER] = "USER_UNIT=",
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
};
static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_SYSTEM] = "UNIT=%s",
[MANAGER_USER] = "USER_UNIT=%s",
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
};
2010-01-28 02:01:15 +01:00
Manager *m;
int r;
2010-03-31 16:29:55 +02:00
assert(_m);
2010-04-06 23:55:42 +02:00
assert(running_as >= 0);
assert(running_as < _MANAGER_RUNNING_AS_MAX);
2010-01-28 02:01:15 +01:00
m = new0(Manager, 1);
if (!m)
2010-03-31 16:29:55 +02:00
return -ENOMEM;
2009-11-18 00:42:52 +01:00
#ifdef ENABLE_EFI
if (running_as == MANAGER_SYSTEM && detect_container() <= 0)
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
#endif
2010-04-06 23:55:42 +02:00
m->running_as = running_as;
2010-04-21 03:27:44 +02:00
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
m->default_tasks_accounting = true;
m->default_tasks_max = UINT64_C(512);
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
/* Prepare log fields we can use for structured logging */
m->unit_log_field = unit_log_fields[running_as];
m->unit_log_format_string = unit_log_format_strings[running_as];
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
core: unified cgroup hierarchy support This patch set adds full support the new unified cgroup hierarchy logic of modern kernels. A new kernel command line option "systemd.unified_cgroup_hierarchy=1" is added. If specified the unified hierarchy is mounted to /sys/fs/cgroup instead of a tmpfs. No further hierarchies are mounted. The kernel command line option defaults to off. We can turn it on by default as soon as the kernel's APIs regarding this are stabilized (but even then downstream distros might want to turn this off, as this will break any tools that access cgroupfs directly). It is possibly to choose for each boot individually whether the unified or the legacy hierarchy is used. nspawn will by default provide the legacy hierarchy to containers if the host is using it, and the unified otherwise. However it is possible to run containers with the unified hierarchy on a legacy host and vice versa, by setting the $UNIFIED_CGROUP_HIERARCHY environment variable for nspawn to 1 or 0, respectively. The unified hierarchy provides reliable cgroup empty notifications for the first time, via inotify. To make use of this we maintain one manager-wide inotify fd, and each cgroup to it. This patch also removes cg_delete() which is unused now. On kernel 4.2 only the "memory" controller is compatible with the unified hierarchy, hence that's the only controller systemd exposes when booted in unified heirarchy mode. This introduces a new enum for enumerating supported controllers, plus a related enum for the mask bits mapping to it. The core is changed to make use of this everywhere. This moves PID 1 into a new "init.scope" implicit scope unit in the root slice. This is necessary since on the unified hierarchy cgroups may either contain subgroups or processes but not both. PID 1 hence has to move out of the root cgroup (strictly speaking the root cgroup is the only one where processes and subgroups are still allowed, but in order to support containers nicey, we move PID 1 into the new scope in all cases.) This new unit is also used on legacy hierarchy setups. It's actually pretty useful on all systems, as it can then be used to filter journal messages coming from PID 1, and so on. The root slice ("-.slice") is now implicitly created and started (and does not require a unit file on disk anymore), since that's where "init.scope" is located and the slice needs to be started before the scope can. To check whether we are in unified or legacy hierarchy mode we use statfs() on /sys/fs/cgroup. If the .f_type field reports tmpfs we are in legacy mode, if it reports cgroupfs we are in unified mode. This patch set carefuly makes sure that cgls and cgtop continue to work as desired. When invoking nspawn as a service it will implicitly create two subcgroups in the cgroup it is using, one to move the nspawn process into, the other to move the actual container processes into. This is done because of the requirement that cgroups may either contain processes or other subgroups.
2015-09-01 19:22:36 +02:00
m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd =
m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
2010-01-24 00:39:29 +01:00
m->ask_password_inotify_fd = -1;
m->have_ask_password = -EINVAL; /* we don't know */
m->first_boot = -1;
m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX;
m->test_run = test_run;
/* Reboot immediately if the user hits C-A-D more often than 7x per 2s */
RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7);
r = manager_default_environment(m);
if (r < 0)
goto fail;
r = hashmap_ensure_allocated(&m->units, &string_hash_ops);
if (r < 0)
2009-11-18 00:42:52 +01:00
goto fail;
r = hashmap_ensure_allocated(&m->jobs, NULL);
if (r < 0)
2009-11-18 00:42:52 +01:00
goto fail;
r = hashmap_ensure_allocated(&m->cgroup_unit, &string_hash_ops);
if (r < 0)
2010-01-24 00:39:29 +01:00
goto fail;
r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops);
if (r < 0)
goto fail;
r = sd_event_default(&m->event);
if (r < 0)
goto fail;
r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m);
if (r < 0)
goto fail;
r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
goto fail;
r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF);
if (r < 0)
goto fail;
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue");
r = manager_setup_signals(m);
if (r < 0)
2010-01-24 00:39:29 +01:00
goto fail;
r = manager_setup_cgroup(m);
if (r < 0)
2010-03-31 16:29:55 +02:00
goto fail;
r = manager_setup_time_change(m);
if (r < 0)
goto fail;
m->udev = udev_new();
if (!m->udev) {
r = -ENOMEM;
goto fail;
}
/* Note that we set up neither kdbus, nor the notify fd
* here. We do that after deserialization, since they might
* have gotten serialized across the reexec. */
2011-03-30 00:47:50 +02:00
m->taint_usr = dir_is_empty("/usr") > 0;
2010-03-31 16:29:55 +02:00
*_m = m;
return 0;
2009-11-18 00:42:52 +01:00
fail:
manager_free(m);
2010-03-31 16:29:55 +02:00
return r;
2009-11-18 00:42:52 +01:00
}
static int manager_setup_notify(Manager *m) {
int r;
if (m->test_run)
return 0;
if (m->notify_fd < 0) {
_cleanup_close_ int fd = -1;
2015-06-08 20:53:16 +02:00
union sockaddr_union sa = {
.sa.sa_family = AF_UNIX,
};
static const int one = 1;
/* First free all secondary fields */
m->notify_socket = mfree(m->notify_socket);
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
if (m->running_as == MANAGER_SYSTEM)
m->notify_socket = strdup("/run/systemd/notify");
else {
const char *e;
e = getenv("XDG_RUNTIME_DIR");
if (!e) {
log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m");
return -EINVAL;
}
m->notify_socket = strappend(e, "/systemd/notify");
}
if (!m->notify_socket)
return log_oom();
(void) mkdir_parents_label(m->notify_socket, 0755);
(void) unlink(m->notify_socket);
strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1);
r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
if (r < 0)
return log_error_errno(errno, "SO_PASSCRED failed: %m");
m->notify_fd = fd;
fd = -1;
log_debug("Using notification socket %s", m->notify_socket);
}
if (!m->notify_event_source) {
r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_dispatch_notify_fd, m);
if (r < 0)
return log_error_errno(r, "Failed to allocate notify event source: %m");
/* Process signals a bit earlier than SIGCHLD, so that we can
* still identify to which service an exit message belongs */
r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
2015-04-29 16:05:32 +02:00
(void) sd_event_source_set_description(m->notify_event_source, "manager-notify");
}
return 0;
}
static int manager_setup_kdbus(Manager *m) {
_cleanup_free_ char *p = NULL;
assert(m);
if (m->test_run || m->kdbus_fd >= 0)
return 0;
if (!is_kdbus_available())
return -ESOCKTNOSUPPORT;
m->kdbus_fd = bus_kernel_create_bus(
m->running_as == MANAGER_SYSTEM ? "system" : "user",
m->running_as == MANAGER_SYSTEM, &p);
if (m->kdbus_fd < 0)
return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
log_debug("Successfully set up kdbus on %s", p);
return 0;
}
static int manager_connect_bus(Manager *m, bool reexecuting) {
bool try_bus_connect;
assert(m);
if (m->test_run)
return 0;
try_bus_connect =
m->kdbus_fd >= 0 ||
reexecuting ||
(m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
/* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
}
2010-04-06 02:43:58 +02:00
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
Unit *u;
2010-04-06 02:43:58 +02:00
unsigned n = 0;
assert(m);
while ((u = m->cleanup_queue)) {
assert(u->in_cleanup_queue);
2010-04-06 02:43:58 +02:00
unit_free(u);
2010-04-06 02:43:58 +02:00
n++;
}
return n;
}
2010-04-23 18:47:49 +02:00
enum {
GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */
2010-04-23 18:47:49 +02:00
GC_OFFSET_UNSURE, /* No clue */
GC_OFFSET_GOOD, /* We still need this unit */
GC_OFFSET_BAD, /* We don't need this unit anymore */
_GC_OFFSET_MAX
};
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
2010-04-23 18:47:49 +02:00
bool is_bad;
assert(u);
if (u->gc_marker == gc_marker + GC_OFFSET_GOOD ||
u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_IN_PATH)
return;
if (u->in_cleanup_queue)
goto bad;
if (unit_check_gc(u))
goto good;
u->gc_marker = gc_marker + GC_OFFSET_IN_PATH;
2010-04-23 18:47:49 +02:00
is_bad = true;
SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) {
unit_gc_sweep(other, gc_marker);
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
goto good;
2010-04-23 18:47:49 +02:00
if (other->gc_marker != gc_marker + GC_OFFSET_BAD)
2010-04-23 18:47:49 +02:00
is_bad = false;
}
2010-04-23 18:47:49 +02:00
if (is_bad)
goto bad;
/* We were unable to find anything out about this entry, so
* let's investigate it later */
u->gc_marker = gc_marker + GC_OFFSET_UNSURE;
2010-04-23 18:47:49 +02:00
unit_add_to_gc_queue(u);
return;
bad:
2010-04-23 18:47:49 +02:00
/* We definitely know that this one is not useful anymore, so
* let's mark it for deletion */
u->gc_marker = gc_marker + GC_OFFSET_BAD;
2010-04-23 18:47:49 +02:00
unit_add_to_cleanup_queue(u);
return;
good:
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
}
static unsigned manager_dispatch_gc_queue(Manager *m) {
Unit *u;
unsigned n = 0;
2010-04-23 18:47:49 +02:00
unsigned gc_marker;
assert(m);
/* log_debug("Running GC..."); */
2010-04-23 18:47:49 +02:00
m->gc_marker += _GC_OFFSET_MAX;
if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
2010-04-22 02:41:14 +02:00
m->gc_marker = 1;
2010-04-23 18:47:49 +02:00
gc_marker = m->gc_marker;
while ((u = m->gc_queue)) {
assert(u->in_gc_queue);
unit_gc_sweep(u, gc_marker);
2010-04-23 18:47:49 +02:00
LIST_REMOVE(gc_queue, m->gc_queue, u);
u->in_gc_queue = false;
n++;
if (u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
if (u->id)
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(u, "Collecting.");
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
}
}
m->n_in_gc_queue = 0;
return n;
}
2010-04-21 03:27:44 +02:00
static void manager_clear_jobs_and_units(Manager *m) {
Unit *u;
2009-11-18 00:42:52 +01:00
assert(m);
2010-01-26 21:39:06 +01:00
while ((u = hashmap_first(m->units)))
unit_free(u);
manager_dispatch_cleanup_queue(m);
assert(!m->load_queue);
assert(!m->run_queue);
assert(!m->dbus_unit_queue);
assert(!m->dbus_job_queue);
assert(!m->cleanup_queue);
assert(!m->gc_queue);
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
m->n_on_console = 0;
m->n_running_jobs = 0;
2010-04-21 03:27:44 +02:00
}
Manager* manager_free(Manager *m) {
2010-04-21 03:27:44 +02:00
UnitType c;
int i;
2010-01-26 21:39:06 +01:00
if (!m)
return NULL;
2010-04-21 03:27:44 +02:00
manager_clear_jobs_and_units(m);
2010-04-06 02:43:58 +02:00
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
2010-04-21 03:27:44 +02:00
/* If we reexecute ourselves, we keep the root cgroup
* around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
2010-03-31 16:29:55 +02:00
2010-11-11 21:28:33 +01:00
manager_undo_generators(m);
bus_done(m);
2010-01-26 21:39:06 +01:00
hashmap_free(m->units);
2009-11-18 00:42:52 +01:00
hashmap_free(m->jobs);
hashmap_free(m->watch_pids1);
hashmap_free(m->watch_pids2);
hashmap_free(m->watch_bus);
2010-01-24 00:39:29 +01:00
set_free(m->startup_units);
set_free(m->failed_units);
sd_event_source_unref(m->signal_event_source);
sd_event_source_unref(m->notify_event_source);
sd_event_source_unref(m->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
safe_close(m->time_change_fd);
safe_close(m->kdbus_fd);
manager_close_ask_password(m);
manager_close_idle_pipe(m);
udev_unref(m->udev);
sd_event_unref(m->event);
2009-11-18 00:42:52 +01:00
free(m->notify_socket);
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
2010-02-13 01:07:02 +01:00
hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
hashmap_free(m->cgroup_netclass_registry);
free(m->switch_root);
free(m->switch_root_init);
for (i = 0; i < _RLIMIT_MAX; i++)
free(m->rlimit[i]);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
2009-11-18 00:42:52 +01:00
free(m);
return NULL;
2009-11-18 00:42:52 +01:00
}
void manager_enumerate(Manager *m) {
2010-01-29 03:18:09 +01:00
UnitType c;
assert(m);
2010-04-21 03:27:44 +02:00
/* Let's ask every type to load all units from disk/kernel
* that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
continue;
}
2010-01-29 03:18:09 +01:00
if (!unit_vtable[c]->enumerate)
continue;
unit_vtable[c]->enumerate(m);
}
2010-01-29 03:18:09 +01:00
manager_dispatch_load_queue(m);
2010-04-21 03:27:44 +02:00
}
static void manager_coldplug(Manager *m) {
2010-04-21 03:27:44 +02:00
Iterator i;
Unit *u;
char *k;
int r;
2010-04-21 03:27:44 +02:00
assert(m);
2010-01-29 03:18:09 +01:00
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
/* ignore aliases */
if (u->id != k)
2010-01-29 03:18:09 +01:00
continue;
r = unit_coldplug(u);
if (r < 0)
log_warning_errno(r, "We couldn't coldplug %s, proceeding anyway: %m", u->id);
2010-01-29 03:18:09 +01:00
}
2010-04-21 03:27:44 +02:00
}
static void manager_build_unit_path_cache(Manager *m) {
char **i;
_cleanup_closedir_ DIR *d = NULL;
int r;
assert(m);
set_free_free(m->unit_path_cache);
m->unit_path_cache = set_new(&string_hash_ops);
if (!m->unit_path_cache) {
log_error("Failed to allocate unit path cache.");
return;
}
/* This simply builds a list of files we know exist, so that
* we don't always have to go to disk */
STRV_FOREACH(i, m->lookup_paths.unit_path) {
struct dirent *de;
2013-01-02 22:03:35 +01:00
d = opendir(*i);
if (!d) {
if (errno != ENOENT)
log_error_errno(errno, "Failed to open directory %s: %m", *i);
continue;
}
while ((de = readdir(d))) {
char *p;
if (hidden_file(de->d_name))
continue;
p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
if (!p) {
r = -ENOMEM;
goto fail;
}
r = set_consume(m->unit_path_cache, p);
if (r < 0)
goto fail;
}
d = safe_closedir(d);
}
return;
fail:
log_error_errno(r, "Failed to build unit path cache: %m");
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
}
static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
Unit *u;
assert(m);
HASHMAP_FOREACH(u, m->units, i) {
if (fdset_size(fds) <= 0)
break;
if (!UNIT_VTABLE(u)->distribute_fds)
continue;
UNIT_VTABLE(u)->distribute_fds(u, fds);
}
}
2010-04-21 03:27:44 +02:00
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r, q;
assert(m);
2013-05-08 14:29:12 +02:00
dual_timestamp_get(&m->generators_start_timestamp);
r = manager_run_generators(m);
2013-05-08 14:29:12 +02:00
dual_timestamp_get(&m->generators_finish_timestamp);
if (r < 0)
return r;
2010-11-11 21:28:33 +01:00
r = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
NULL,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (r < 0)
return r;
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
* this is already known, so we increase the counter here
* already */
if (serialization)
m->n_reloading ++;
2010-04-21 03:27:44 +02:00
/* First, enumerate what we can from all config files */
dual_timestamp_get(&m->units_load_start_timestamp);
manager_enumerate(m);
dual_timestamp_get(&m->units_load_finish_timestamp);
2010-04-21 03:27:44 +02:00
/* Second, deserialize if there is something to deserialize */
if (serialization)
r = manager_deserialize(m, serialization, fds);
2010-04-21 03:27:44 +02:00
/* 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. */
manager_distribute_fds(m, fds);
/* We might have deserialized the notify fd, but if we didn't
* then let's create the bus now */
q = manager_setup_notify(m);
if (q < 0 && r == 0)
r = q;
/* We might have deserialized the kdbus control fd, but if we
* didn't, then let's create the bus now. */
manager_setup_kdbus(m);
manager_connect_bus(m, !!serialization);
bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
2010-04-21 03:27:44 +02:00
/* Third, fire things up! */
manager_coldplug(m);
2010-04-21 03:27:44 +02:00
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;
}
2010-04-21 03:27:44 +02:00
return r;
2010-01-29 03:18:09 +01:00
}
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) {
2010-01-20 02:12:51 +01:00
int r;
Transaction *tr;
2010-01-20 02:12:51 +01:00
assert(m);
assert(type < _JOB_TYPE_MAX);
2010-01-26 21:39:06 +01:00
assert(unit);
2010-01-20 02:12:51 +01:00
assert(mode < _JOB_MODE_MAX);
2009-11-18 00:42:52 +01:00
if (mode == JOB_ISOLATE && type != JOB_START)
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
if (mode == JOB_ISOLATE && !unit->allow_isolate)
return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
2010-08-30 22:45:46 +02:00
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
2010-01-29 04:11:36 +01:00
type = job_type_collapse(type, unit);
core: add NOP jobs, job type collapsing Two of our current job types are special: JOB_TRY_RESTART, JOB_RELOAD_OR_START. They differ from other job types by being sensitive to the unit active state. They perform some action when the unit is active and some other action otherwise. This raises a question: when exactly should the unit state be checked to make the decision? Currently the unit state is checked when the job becomes runnable. It's more sensible to check the state immediately when the job is added by the user. When the user types "systemctl try-restart foo.service", he really intends to restart the service if it's running right now. If it isn't running right now, the restart is pointless. Consider the example (from Bugzilla[1]): sleep.service takes some time to start. hello.service has After=sleep.service. Both services get started. Two jobs will appear: hello.service/start waiting sleep.service/start running Then someone runs "systemctl try-restart hello.service". Currently the try-restart operation will block and wait for sleep.service/start to complete. The correct result is to complete the try-restart operation immediately with success, because hello.service is not running. The two original jobs must not be disturbed by this. To fix this we introduce two new concepts: - a new job type: JOB_NOP A JOB_NOP job does not do anything to the unit. It does not pull in any dependencies. It is always immediately runnable. When installed to a unit, it sits in a special slot (u->nop_job) where it never conflicts with the installed job (u->job) of a different type. It never merges with jobs of other types, but it can merge into an already installed JOB_NOP job. - "collapsing" of job types When a job of one of the two special types is added, the state of the unit is checked immediately and the job type changes: JOB_TRY_RESTART -> JOB_RESTART or JOB_NOP JOB_RELOAD_OR_START -> JOB_RELOAD or JOB_START Should a job type JOB_RELOAD_OR_START appear later during job merging, it collapses immediately afterwards. Collapsing actually makes some things simpler, because there are now fewer job types that are allowed in the transaction. [1] Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=753586
2012-04-25 11:58:27 +02:00
tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
return -ENOMEM;
2010-01-19 04:15:20 +01:00
r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false,
mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
mode == JOB_IGNORE_DEPENDENCIES, e);
if (r < 0)
goto tr_abort;
if (mode == JOB_ISOLATE) {
r = transaction_add_isolate_jobs(tr, m);
if (r < 0)
goto tr_abort;
}
r = transaction_activate(tr, m, mode, e);
if (r < 0)
goto tr_abort;
2010-01-20 02:12:51 +01:00
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(unit,
2013-01-05 18:00:35 +01:00
"Enqueued job %s/%s as %u", unit->id,
job_type_to_string(type), (unsigned) tr->anchor_job->id);
2010-01-29 03:18:09 +01:00
2010-01-20 02:12:51 +01:00
if (_ret)
*_ret = tr->anchor_job;
2009-11-18 00:42:52 +01:00
transaction_free(tr);
2010-01-20 02:12:51 +01:00
return 0;
tr_abort:
transaction_abort(tr);
transaction_free(tr);
return r;
2010-01-20 02:12:51 +01:00
}
2009-11-18 00:42:52 +01:00
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) {
Unit *unit;
int r;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(name);
assert(mode < _JOB_MODE_MAX);
r = manager_load_unit(m, name, NULL, NULL, &unit);
if (r < 0)
return r;
return manager_add_job(m, type, unit, mode, e, ret);
}
int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(name);
assert(mode < _JOB_MODE_MAX);
r = manager_add_job_by_name(m, type, name, mode, &error, ret);
if (r < 0)
return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r));
return r;
}
2009-11-18 00:42:52 +01:00
Job *manager_get_job(Manager *m, uint32_t id) {
assert(m);
return hashmap_get(m->jobs, UINT32_TO_PTR(id));
}
2010-01-26 21:39:06 +01:00
Unit *manager_get_unit(Manager *m, const char *name) {
2009-11-18 00:42:52 +01:00
assert(m);
assert(name);
2010-01-26 21:39:06 +01:00
return hashmap_get(m->units, name);
2009-11-18 00:42:52 +01:00
}
unsigned manager_dispatch_load_queue(Manager *m) {
Unit *u;
unsigned n = 0;
2009-11-18 00:42:52 +01:00
assert(m);
/* Make sure we are not run recursively */
if (m->dispatching_load_queue)
return 0;
m->dispatching_load_queue = true;
2010-01-26 21:39:06 +01:00
/* Dispatches the load queue. Takes a unit from the queue and
2009-11-18 00:42:52 +01:00
* tries to load its data until the queue is empty */
while ((u = m->load_queue)) {
assert(u->in_load_queue);
unit_load(u);
n++;
2009-11-18 00:42:52 +01:00
}
m->dispatching_load_queue = false;
return n;
2009-11-18 00:42:52 +01:00
}
int manager_load_unit_prepare(
Manager *m,
const char *name,
const char *path,
sd_bus_error *e,
Unit **_ret) {
2010-01-26 21:39:06 +01:00
Unit *ret;
UnitType t;
2009-11-18 00:42:52 +01:00
int r;
assert(m);
2010-04-15 03:11:11 +02:00
assert(name || path);
2009-11-18 00:42:52 +01:00
/* This will prepare the unit for loading, but not actually
* load anything from disk. */
2010-01-27 00:15:56 +01:00
if (path && !is_path(path))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
2010-04-15 03:11:11 +02:00
if (!name)
name = basename(path);
2010-04-15 03:11:11 +02:00
t = unit_name_to_type(name);
if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
2009-11-18 00:42:52 +01:00
ret = manager_get_unit(m, name);
if (ret) {
*_ret = ret;
return 1;
}
2009-11-18 00:42:52 +01:00
ret = unit_new(m, unit_vtable[t]->object_size);
if (!ret)
2009-11-18 00:42:52 +01:00
return -ENOMEM;
if (path) {
ret->fragment_path = strdup(path);
if (!ret->fragment_path) {
2010-01-27 00:15:56 +01:00
unit_free(ret);
return -ENOMEM;
}
}
2010-01-27 00:15:56 +01:00
r = unit_add_name(ret, name);
if (r < 0) {
2010-01-26 21:39:06 +01:00
unit_free(ret);
2010-01-21 00:51:37 +01:00
return r;
2009-11-18 00:42:52 +01:00
}
2010-01-26 21:39:06 +01:00
unit_add_to_load_queue(ret);
unit_add_to_dbus_queue(ret);
unit_add_to_gc_queue(ret);
if (_ret)
*_ret = ret;
return 0;
}
int manager_load_unit(
Manager *m,
const char *name,
const char *path,
sd_bus_error *e,
Unit **_ret) {
int r;
assert(m);
/* This will load the service information files, but not actually
* start any services or anything. */
r = manager_load_unit_prepare(m, name, path, e, _ret);
if (r != 0)
return r;
2010-01-29 03:18:09 +01:00
manager_dispatch_load_queue(m);
2009-11-18 00:42:52 +01:00
2010-04-15 03:11:11 +02:00
if (_ret)
*_ret = unit_follow_merge(*_ret);
2010-04-15 03:11:11 +02:00
2009-11-18 00:42:52 +01:00
return 0;
}
2010-01-19 00:22:34 +01:00
2010-01-20 04:02:39 +01:00
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
Iterator i;
2010-01-19 00:22:34 +01:00
Job *j;
assert(s);
assert(f);
HASHMAP_FOREACH(j, s->jobs, i)
2010-01-20 04:02:39 +01:00
job_dump(j, f, prefix);
2010-01-19 00:22:34 +01:00
}
2010-01-26 21:39:06 +01:00
void manager_dump_units(Manager *s, FILE *f, const char *prefix) {
Iterator i;
2010-01-26 21:39:06 +01:00
Unit *u;
2010-01-19 04:15:20 +01:00
const char *t;
2010-01-19 00:22:34 +01:00
assert(s);
assert(f);
2010-01-26 21:39:06 +01:00
HASHMAP_FOREACH_KEY(u, t, s->units, i)
if (u->id == t)
2010-01-26 21:39:06 +01:00
unit_dump(u, f, prefix);
2010-01-19 00:22:34 +01:00
}
void manager_clear_jobs(Manager *m) {
Job *j;
assert(m);
while ((j = hashmap_first(m->jobs)))
/* No need to recurse. We're cancelling all jobs. */
job_finish_and_invalidate(j, JOB_CANCELED, false);
}
2010-01-23 22:56:47 +01:00
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
Manager *m = userdata;
2010-01-23 22:56:47 +01:00
Job *j;
assert(source);
assert(m);
2010-01-24 00:39:29 +01:00
while ((j = m->run_queue)) {
2010-01-26 19:25:02 +01:00
assert(j->installed);
assert(j->in_run_queue);
job_run_and_invalidate(j);
2010-01-24 00:39:29 +01:00
}
if (m->n_running_jobs > 0)
manager_watch_jobs_in_progress(m);
if (m->n_on_console > 0)
manager_watch_idle_pipe(m);
return 1;
}
static unsigned manager_dispatch_dbus_queue(Manager *m) {
Job *j;
Unit *u;
unsigned n = 0;
assert(m);
if (m->dispatching_dbus_queue)
return 0;
m->dispatching_dbus_queue = true;
while ((u = m->dbus_unit_queue)) {
assert(u->in_dbus_queue);
bus_unit_send_change_signal(u);
n++;
}
while ((j = m->dbus_job_queue)) {
assert(j->in_dbus_queue);
bus_job_send_change_signal(j);
n++;
}
m->dispatching_dbus_queue = false;
if (m->send_reloading_done) {
m->send_reloading_done = false;
bus_manager_send_reloading(m, false);
}
if (m->queued_message)
bus_send_queued_message(m);
return n;
2010-01-24 00:39:29 +01:00
}
2015-10-28 12:43:52 +01:00
static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) {
_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;
}
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
else
log_unit_debug(u, "Got notification message for unit. Ignoring.");
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
_cleanup_fdset_free_ FDSet *fds = NULL;
Manager *m = userdata;
char buf[NOTIFY_BUFFER_MAX+1];
struct iovec iovec = {
.iov_base = buf,
.iov_len = sizeof(buf)-1,
};
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
} control = {};
struct msghdr msghdr = {
.msg_iov = &iovec,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
struct ucred *ucred = NULL;
bool found = false;
Unit *u1, *u2, *u3;
int r, *fd_array = NULL;
unsigned n_fds = 0;
ssize_t n;
assert(m);
assert(m->notify_fd == fd);
if (revents != EPOLLIN) {
log_warning("Got unexpected poll event for notify fd.");
return 0;
}
n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (n < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return -errno;
}
CMSG_FOREACH(cmsg, &msghdr) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
fd_array = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
} else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
ucred = (struct ucred*) CMSG_DATA(cmsg);
}
}
if (n_fds > 0) {
assert(fd_array);
r = fdset_new_array(&fds, fd_array, n_fds);
if (r < 0) {
close_many(fd_array, n_fds);
return log_oom();
}
}
if (!ucred || ucred->pid <= 0) {
log_warning("Received notify message without valid credentials. Ignoring.");
return 0;
}
if ((size_t) n >= sizeof(buf)) {
log_warning("Received notify message exceeded maximum size. Ignoring.");
return 0;
}
buf[n] = 0;
/* Notify every unit that might be interested, but try
* to avoid notifying the same one multiple times. */
u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
if (u1) {
manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
found = true;
}
u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
if (u2 && u2 != u1) {
manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
found = true;
}
u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
if (u3 && u3 != u2 && u3 != u1) {
manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
found = true;
}
if (!found)
log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
if (fdset_size(fds) > 0)
log_warning("Got auxiliary fds with notification message, closing all.");
return 0;
}
2015-10-28 12:43:52 +01:00
static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
assert(m);
assert(u);
assert(si);
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_unit_debug(u, "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) {
2010-01-24 00:39:29 +01:00
assert(m);
for (;;) {
siginfo_t si = {};
2010-01-24 00:39:29 +01:00
/* 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. */
if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
2010-01-27 04:31:52 +01:00
if (errno == ECHILD)
break;
if (errno == EINTR)
continue;
2010-01-24 00:39:29 +01:00
return -errno;
2010-01-27 04:31:52 +01:00
}
2010-01-24 00:39:29 +01:00
if (si.si_pid <= 0)
2010-01-24 00:39:29 +01:00
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
_cleanup_free_ char *name = NULL;
Unit *u1, *u2, *u3;
get_process_comm(si.si_pid, &name);
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... */
u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
if (u1)
invoke_sigchld_event(m, u1, &si);
u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
if (u2 && u2 != u1)
invoke_sigchld_event(m, u2, &si);
u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
if (u3 && u3 != u2 && u3 != u1)
invoke_sigchld_event(m, u3, &si);
}
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
if (errno == EINTR)
continue;
return -errno;
}
2010-01-24 00:39:29 +01:00
}
return 0;
}
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_debug("Activating special unit %s", name);
r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL);
2013-01-02 22:03:35 +01:00
if (r < 0)
core,network: major per-object logging rework This changes log_unit_info() (and friends) to take a real Unit* object insted of just a unit name as parameter. The call will now prefix all logged messages with the unit name, thus allowing the unit name to be dropped from the various passed romat strings, simplifying invocations drastically, and unifying log output across messages. Also, UNIT= vs. USER_UNIT= is now derived from the Manager object attached to the Unit object, instead of getpid(). This has the benefit of correcting the field for --test runs. Also contains a couple of other logging improvements: - Drops a couple of strerror() invocations in favour of using %m. - Not only .mount units now warn if a symlinks exist for the mount point already, .automount units do that too, now. - A few invocations of log_struct() that didn't actually pass any additional structured data have been replaced by simpler invocations of log_unit_info() and friends. - For structured data a new LOG_UNIT_MESSAGE() macro has been added, that works like LOG_MESSAGE() but prefixes the message with the unit name. Similar, there's now LOG_LINK_MESSAGE() and LOG_NETDEV_MESSAGE(). - For structured data new LOG_UNIT_ID(), LOG_LINK_INTERFACE(), LOG_NETDEV_INTERFACE() macros have been added that generate the necessary per object fields. The old log_unit_struct() call has been removed in favour of these new macros used in raw log_struct() invocations. In addition to removing one more function call this allows generated structured log messages that contain two object fields, as necessary for example for network interfaces that are joined into another network interface, and whose messages shall be indexed by both. - The LOG_ERRNO() macro has been removed, in favour of log_struct_errno(). The latter has the benefit of ensuring that %m in format strings is properly resolved to the specified error number. - A number of logging messages have been converted to use log_unit_info() instead of log_info() - The client code in sysv-generator no longer #includes core code from src/core/. - log_unit_full_errno() has been removed, log_unit_full() instead takes an errno now, too. - log_unit_info(), log_link_info(), log_netdev_info() and friends, now avoid double evaluation of their parameters
2015-05-11 20:38:21 +02:00
log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
return r;
}
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
2010-01-24 00:39:29 +01:00
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
int r;
2010-01-24 00:39:29 +01:00
assert(m);
assert(m->signal_fd == fd);
if (revents != EPOLLIN) {
log_warning("Got unexpected events from signal file descriptor.");
return 0;
}
2010-01-24 00:39:29 +01:00
for (;;) {
n = read(m->signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
2010-01-24 00:39:29 +01:00
if (n >= 0)
return -EIO;
if (errno == EINTR || errno == EAGAIN)
2010-01-27 04:31:52 +01:00
break;
2010-01-24 00:39:29 +01:00
return -errno;
}
2014-02-10 02:08:55 +01:00
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
(sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
2014-02-10 02:08:55 +01:00
? LOG_DEBUG : LOG_INFO,
&sfsi);
2010-01-27 04:36:30 +01:00
switch (sfsi.ssi_signo) {
case SIGCHLD:
2010-01-24 00:39:29 +01:00
sigchld = true;
2010-01-27 04:36:30 +01:00
break;
2010-01-27 05:31:53 +01:00
case SIGTERM:
if (m->running_as == MANAGER_SYSTEM) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
break;
}
/* Fall through */
case SIGINT:
if (m->running_as == MANAGER_SYSTEM) {
2015-01-28 14:03:24 +01:00
/* If the user presses C-A-D more than
* 7 times within 2s, we reboot
* immediately. */
if (ratelimit_test(&m->ctrl_alt_del_ratelimit))
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
else {
log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
m->exit_code = MANAGER_REBOOT;
}
break;
}
/* Run the exit target if there is one, if not, just exit. */
if (manager_start_target(m, SPECIAL_EXIT_TARGET, JOB_REPLACE) < 0) {
m->exit_code = MANAGER_EXIT;
return 0;
}
break;
case SIGWINCH:
if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGPWR:
if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
2010-01-27 05:31:53 +01:00
case SIGUSR1: {
2010-04-13 03:20:22 +02:00
Unit *u;
u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
log_info("Trying to reconnect to bus...");
bus_init(m, true);
2010-04-13 03:20:22 +02:00
}
if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
log_info("Loading D-Bus service...");
manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
2010-04-13 03:20:22 +02:00
}
break;
}
case SIGUSR2: {
_cleanup_free_ char *dump = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t size;
f = open_memstream(&dump, &size);
if (!f) {
log_warning_errno(errno, "Failed to allocate memory stream: %m");
break;
}
manager_dump_units(m, f, "\t");
manager_dump_jobs(m, f, "\t");
r = fflush_and_check(f);
if (r < 0) {
log_warning_errno(r, "Failed to write status stream: %m");
break;
}
log_dump(LOG_INFO, dump);
break;
}
2010-04-21 03:27:44 +02:00
case SIGHUP:
m->exit_code = MANAGER_RELOAD;
break;
default: {
/* Starting SIGRTMIN+0 */
static const char * const target_table[] = {
[0] = SPECIAL_DEFAULT_TARGET,
[1] = SPECIAL_RESCUE_TARGET,
2010-07-12 23:49:20 +02:00
[2] = SPECIAL_EMERGENCY_TARGET,
[3] = SPECIAL_HALT_TARGET,
[4] = SPECIAL_POWEROFF_TARGET,
[5] = SPECIAL_REBOOT_TARGET,
[6] = SPECIAL_KEXEC_TARGET
};
/* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
static const ManagerExitCode code_table[] = {
[0] = MANAGER_HALT,
[1] = MANAGER_POWEROFF,
[2] = MANAGER_REBOOT,
[3] = MANAGER_KEXEC
};
if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
(int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
int idx = (int) sfsi.ssi_signo - SIGRTMIN;
manager_start_target(m, target_table[idx],
(idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE);
break;
}
if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
(int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
break;
}
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
manager_set_show_status(m, SHOW_STATUS_YES);
break;
case 21:
manager_set_show_status(m, SHOW_STATUS_NO);
break;
case 22:
log_set_max_level(LOG_DEBUG);
log_info("Setting log level to debug.");
break;
case 23:
log_set_max_level(LOG_INFO);
log_info("Setting log level to info.");
break;
case 24:
if (m->running_as == MANAGER_USER) {
m->exit_code = MANAGER_EXIT;
return 0;
}
/* This is a nop on init */
break;
case 26:
case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
log_notice("Setting log target to journal-or-kmsg.");
break;
case 27:
log_set_target(LOG_TARGET_CONSOLE);
log_notice("Setting log target to console.");
break;
case 28:
log_set_target(LOG_TARGET_KMSG);
log_notice("Setting log target to kmsg.");
break;
default:
log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
}
2010-01-27 04:36:30 +01:00
}
}
2010-01-24 00:39:29 +01:00
}
if (sigchld)
manager_dispatch_sigchld(m);
return 0;
}
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
Iterator i;
Unit *u;
assert(m);
assert(m->time_change_fd == fd);
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
LOG_MESSAGE("Time has been changed"),
NULL);
/* Restart the watch */
m->time_change_event_source = sd_event_source_unref(m->time_change_event_source);
m->time_change_fd = safe_close(m->time_change_fd);
2010-01-29 06:04:08 +01:00
manager_setup_time_change(m);
HASHMAP_FOREACH(u, m->units, i)
if (UNIT_VTABLE(u)->time_change)
UNIT_VTABLE(u)->time_change(u);
return 0;
}
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
assert(m);
assert(m->idle_pipe[2] == fd);
m->no_console_output = m->n_on_console > 0;
manager_close_idle_pipe(m);
return 0;
}
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);
2010-01-24 00:39:29 +01:00
manager_print_jobs_in_progress(m);
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);
2010-01-24 00:39:29 +01:00
}
int manager_loop(Manager *m) {
int r;
2011-03-30 20:15:45 +02:00
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
2010-01-24 00:39:29 +01:00
assert(m);
m->exit_code = MANAGER_OK;
2010-01-24 00:39:29 +01:00
/* Release the path cache */
2015-10-28 18:23:07 +01:00
m->unit_path_cache = set_free_free(m->unit_path_cache);
2010-09-21 04:14:38 +02:00
manager_check_finished(m);
/* There might still be some zombies hanging around from
2013-11-06 11:03:18 +01:00
* before we were exec()'ed. Let's reap them. */
r = manager_dispatch_sigchld(m);
if (r < 0)
return r;
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
2010-01-24 00:39:29 +01:00
if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
watchdog_ping();
if (!ratelimit_test(&rl)) {
/* Yay, something is going seriously wrong, pause a little */
log_warning("Looping too fast. Throttling execution a little.");
sleep(1);
}
if (manager_dispatch_load_queue(m) > 0)
2010-04-06 02:43:58 +02:00
continue;
if (manager_dispatch_gc_queue(m) > 0)
continue;
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
if (manager_dispatch_cgroup_queue(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
continue;
/* Sleep for half the watchdog time */
if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
} else
wait_usec = USEC_INFINITY;
2010-01-24 00:39:29 +01:00
r = sd_event_run(m->event, wait_usec);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
2010-04-21 03:27:44 +02:00
}
2010-04-21 03:27:44 +02:00
return m->exit_code;
2010-01-23 22:56:47 +01:00
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) {
_cleanup_free_ char *n = NULL;
Unit *u;
int r;
assert(m);
assert(s);
assert(_u);
r = unit_name_from_dbus_path(s, &n);
if (r < 0)
return r;
r = manager_load_unit(m, n, NULL, e, &u);
if (r < 0)
return r;
*_u = u;
return 0;
}
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
const char *p;
unsigned id;
Job *j;
int r;
assert(m);
assert(s);
assert(_j);
p = startswith(s, "/org/freedesktop/systemd1/job/");
if (!p)
return -EINVAL;
r = safe_atou(p, &id);
if (r < 0)
return r;
j = manager_get_job(m, id);
if (!j)
return -ENOENT;
*_j = j;
return 0;
}
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
_cleanup_free_ char *p = NULL;
const char *msg;
int audit_fd, r;
if (m->running_as != MANAGER_SYSTEM)
return;
audit_fd = get_audit_fd();
if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
* started and we're just deserializing */
if (m->n_reloading > 0)
return;
if (u->type != UNIT_SERVICE)
return;
r = unit_name_to_prefix_and_instance(u->id, &p);
if (r < 0) {
log_error_errno(r, "Failed to extract prefix and instance of unit name: %m");
return;
}
msg = strjoina("unit=", p);
if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) {
if (errno == EPERM)
/* We aren't allowed to send audit messages?
* Then let's not retry again. */
close_audit_fd();
else
log_warning_errno(errno, "Failed to send audit message: %m");
}
#endif
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
2014-08-03 18:52:03 +02:00
union sockaddr_union sa = PLYMOUTH_SOCKET;
int n = 0;
_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 */
if (m->n_reloading > 0)
return;
if (m->running_as != MANAGER_SYSTEM)
return;
if (detect_container() > 0)
return;
if (u->type != UNIT_SERVICE &&
u->type != UNIT_MOUNT &&
u->type != UNIT_SWAP)
return;
/* We set SOCK_NONBLOCK here so that we rather drop the
* message then wait for plymouth */
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
log_error_errno(errno, "socket() failed: %m");
return;
}
2010-11-16 03:12:54 +01:00
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error_errno(errno, "connect() failed: %m");
return;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
log_oom();
return;
}
errno = 0;
if (write(fd, message, n + 1) != n + 1)
if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error_errno(errno, "Failed to write Plymouth message: %m");
}
int manager_open_serialization(Manager *m, FILE **_f) {
const char *path;
int fd = -1;
2010-04-21 03:27:44 +02:00
FILE *f;
assert(_f);
path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
2010-04-21 03:27:44 +02:00
return -errno;
log_debug("Serializing state to %s", path);
f = fdopen(fd, "w+");
if (!f) {
safe_close(fd);
2010-04-21 03:27:44 +02:00
return -errno;
}
2010-04-21 03:27:44 +02:00
*_f = f;
return 0;
}
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
2010-04-21 03:27:44 +02:00
Iterator i;
Unit *u;
const char *t;
char **e;
2010-04-21 03:27:44 +02:00
int r;
assert(m);
assert(f);
assert(fds);
m->n_reloading ++;
fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp);
dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp);
dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp);
dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
if (!in_initrd()) {
dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp);
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
dual_timestamp_serialize(f, "security-start-timestamp", &m->security_start_timestamp);
dual_timestamp_serialize(f, "security-finish-timestamp", &m->security_finish_timestamp);
dual_timestamp_serialize(f, "generators-start-timestamp", &m->generators_start_timestamp);
dual_timestamp_serialize(f, "generators-finish-timestamp", &m->generators_finish_timestamp);
dual_timestamp_serialize(f, "units-load-start-timestamp", &m->units_load_start_timestamp);
dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp);
}
if (!switching_root) {
STRV_FOREACH(e, m->environment) {
_cleanup_free_ char *ce;
ce = cescape(*e);
if (!ce)
return -ENOMEM;
fprintf(f, "env=%s\n", *e);
}
}
if (m->notify_fd >= 0) {
int copy;
copy = fdset_put_dup(fds, m->notify_fd);
if (copy < 0)
return copy;
fprintf(f, "notify-fd=%i\n", copy);
fprintf(f, "notify-socket=%s\n", m->notify_socket);
}
if (m->kdbus_fd >= 0) {
int copy;
copy = fdset_put_dup(fds, m->kdbus_fd);
if (copy < 0)
return copy;
fprintf(f, "kdbus-fd=%i\n", copy);
}
bus_track_serialize(m->subscribed, f);
fputc('\n', f);
2010-04-21 03:27:44 +02:00
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
if (u->id != t)
2010-04-21 03:27:44 +02:00
continue;
/* Start marker */
fputs(u->id, f);
2010-04-21 03:27:44 +02:00
fputc('\n', f);
r = unit_serialize(u, f, fds, !switching_root);
if (r < 0) {
m->n_reloading --;
2010-04-21 03:27:44 +02:00
return r;
}
2010-04-21 03:27:44 +02:00
}
assert(m->n_reloading > 0);
m->n_reloading --;
2010-04-21 03:27:44 +02:00
if (ferror(f))
return -EIO;
r = bus_fdset_add_all(m, fds);
if (r < 0)
return r;
2010-04-21 03:27:44 +02:00
return 0;
}
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int r = 0;
assert(m);
assert(f);
log_debug("Deserializing state...");
m->n_reloading ++;
2010-07-10 04:51:03 +02:00
for (;;) {
char line[LINE_MAX], *l;
if (!fgets(line, sizeof(line), f)) {
if (feof(f))
r = 0;
else
r = -errno;
goto finish;
}
char_array_0(line);
l = strstrip(line);
if (l[0] == 0)
break;
if (startswith(l, "current-job-id=")) {
uint32_t id;
if (safe_atou32(l+15, &id) < 0)
log_debug("Failed to parse current job id value %s", l+15);
else
m->current_job_id = MAX(m->current_job_id, id);
} else if (startswith(l, "n-installed-jobs=")) {
uint32_t n;
if (safe_atou32(l+17, &n) < 0)
log_debug("Failed to parse installed jobs counter %s", l+17);
else
m->n_installed_jobs += n;
} else if (startswith(l, "n-failed-jobs=")) {
uint32_t n;
if (safe_atou32(l+14, &n) < 0)
log_debug("Failed to parse failed jobs counter %s", l+14);
else
m->n_failed_jobs += n;
} else if (startswith(l, "taint-usr=")) {
int b;
b = parse_boolean(l+10);
if (b < 0)
log_debug("Failed to parse taint /usr flag %s", l+10);
else
m->taint_usr = m->taint_usr || b;
} else if (startswith(l, "firmware-timestamp="))
dual_timestamp_deserialize(l+19, &m->firmware_timestamp);
else if (startswith(l, "loader-timestamp="))
dual_timestamp_deserialize(l+17, &m->loader_timestamp);
else if (startswith(l, "kernel-timestamp="))
dual_timestamp_deserialize(l+17, &m->kernel_timestamp);
else if (startswith(l, "initrd-timestamp="))
dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
else if (startswith(l, "userspace-timestamp="))
dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
2010-10-27 00:01:12 +02:00
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
else if (startswith(l, "security-start-timestamp="))
dual_timestamp_deserialize(l+25, &m->security_start_timestamp);
else if (startswith(l, "security-finish-timestamp="))
dual_timestamp_deserialize(l+26, &m->security_finish_timestamp);
else if (startswith(l, "generators-start-timestamp="))
dual_timestamp_deserialize(l+27, &m->generators_start_timestamp);
else if (startswith(l, "generators-finish-timestamp="))
dual_timestamp_deserialize(l+28, &m->generators_finish_timestamp);
else if (startswith(l, "units-load-start-timestamp="))
dual_timestamp_deserialize(l+27, &m->units_load_start_timestamp);
else if (startswith(l, "units-load-finish-timestamp="))
dual_timestamp_deserialize(l+28, &m->units_load_finish_timestamp);
else if (startswith(l, "env=")) {
_cleanup_free_ char *uce = NULL;
char **e;
r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
if (r < 0)
goto finish;
e = strv_env_set(m->environment, uce);
if (!e) {
r = -ENOMEM;
goto finish;
}
strv_free(m->environment);
m->environment = e;
} else if (startswith(l, "notify-fd=")) {
int fd;
if (safe_atoi(l + 10, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_debug("Failed to parse notify fd: %s", l + 10);
else {
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
safe_close(m->notify_fd);
m->notify_fd = fdset_remove(fds, fd);
}
} else if (startswith(l, "notify-socket=")) {
char *n;
n = strdup(l+14);
if (!n) {
r = -ENOMEM;
goto finish;
}
free(m->notify_socket);
m->notify_socket = n;
} else if (startswith(l, "kdbus-fd=")) {
int fd;
if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_debug("Failed to parse kdbus fd: %s", l + 9);
else {
safe_close(m->kdbus_fd);
m->kdbus_fd = fdset_remove(fds, fd);
}
} else {
int k;
k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
if (k < 0)
log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
else if (k == 0)
log_debug("Unknown serialization item '%s'", l);
}
}
2010-04-21 03:27:44 +02:00
for (;;) {
Unit *u;
char name[UNIT_NAME_MAX+2];
/* Start marker */
if (!fgets(name, sizeof(name), f)) {
if (feof(f))
r = 0;
else
r = -errno;
2010-04-21 03:27:44 +02:00
2010-07-10 04:51:03 +02:00
goto finish;
2010-04-21 03:27:44 +02:00
}
char_array_0(name);
2013-01-02 22:03:35 +01:00
r = manager_load_unit(m, strstrip(name), NULL, NULL, &u);
if (r < 0)
2010-07-10 04:51:03 +02:00
goto finish;
2010-04-21 03:27:44 +02:00
r = unit_deserialize(u, f, fds);
if (r < 0)
2010-07-10 04:51:03 +02:00
goto finish;
2010-04-21 03:27:44 +02:00
}
finish:
2013-12-10 18:28:26 +01:00
if (ferror(f))
2010-07-10 04:51:03 +02:00
r = -EIO;
2010-04-21 03:27:44 +02:00
assert(m->n_reloading > 0);
m->n_reloading --;
2010-07-10 04:51:03 +02:00
return r;
2010-04-21 03:27:44 +02:00
}
int manager_reload(Manager *m) {
int r, q;
2013-10-12 01:33:48 +02:00
_cleanup_fclose_ FILE *f = NULL;
_cleanup_fdset_free_ FDSet *fds = NULL;
2010-04-21 03:27:44 +02:00
assert(m);
r = manager_open_serialization(m, &f);
if (r < 0)
2010-04-21 03:27:44 +02:00
return r;
m->n_reloading ++;
bus_manager_send_reloading(m, true);
fds = fdset_new();
if (!fds) {
m->n_reloading --;
2013-10-12 01:33:48 +02:00
return -ENOMEM;
2010-04-21 03:27:44 +02:00
}
r = manager_serialize(m, f, fds, false);
if (r < 0) {
m->n_reloading --;
2013-10-12 01:33:48 +02:00
return r;
}
2010-04-21 03:27:44 +02:00
if (fseeko(f, 0, SEEK_SET) < 0) {
m->n_reloading --;
2013-10-12 01:33:48 +02:00
return -errno;
2010-04-21 03:27:44 +02:00
}
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
2010-11-11 21:28:33 +01:00
manager_undo_generators(m);
lookup_paths_free(&m->lookup_paths);
/* Find new unit paths */
q = manager_run_generators(m);
if (q < 0 && r >= 0)
r = q;
2010-11-11 21:28:33 +01:00
q = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
NULL,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (q < 0 && r >= 0)
r = q;
2010-11-11 21:28:33 +01:00
manager_build_unit_path_cache(m);
2010-04-21 03:27:44 +02:00
/* First, enumerate what we can from all config files */
manager_enumerate(m);
2010-04-21 03:27:44 +02:00
/* Second, deserialize our stored data */
q = manager_deserialize(m, f, fds);
if (q < 0 && r >= 0)
2010-04-21 03:27:44 +02:00
r = q;
fclose(f);
f = NULL;
/* Re-register notify_fd as event source */
q = manager_setup_notify(m);
if (q < 0 && r >= 0)
r = q;
2010-04-21 03:27:44 +02:00
/* Third, fire things up! */
manager_coldplug(m);
2010-04-21 03:27:44 +02:00
assert(m->n_reloading > 0);
m->n_reloading--;
m->send_reloading_done = true;
2010-04-21 03:27:44 +02:00
return r;
}
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;
assert(m);
HASHMAP_FOREACH(u, m->units, i)
unit_reset_failed(u);
}
bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
Unit *u;
assert(m);
assert(name);
/* Returns true if the unit is inactive or going down */
2013-01-02 22:03:35 +01:00
u = manager_get_unit(m, name);
if (!u)
return true;
return unit_inactive_or_pending(u);
}
static void manager_notify_finished(Manager *m) {
2012-09-13 19:29:46 +02:00
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
2010-09-21 04:14:38 +02:00
if (m->test_run)
2010-09-21 04:14:38 +02:00
return;
if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) {
/* Note that m->kernel_usec.monotonic is always at 0,
* and m->firmware_usec.monotonic and
* m->loader_usec.monotonic should be considered
* negative values. */
2012-09-13 19:29:46 +02:00
firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic;
loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic;
userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
2012-09-13 19:29:46 +02:00
total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic;
if (dual_timestamp_is_set(&m->initrd_timestamp)) {
kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic;
initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"KERNEL_USEC="USEC_FMT, kernel_usec,
"INITRD_USEC="USEC_FMT, initrd_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
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;
initrd_usec = 0;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"KERNEL_USEC="USEC_FMT, kernel_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.",
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 {
firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
NULL);
}
2010-09-21 04:14:38 +02:00
bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
"READY=1\n"
"STATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
2010-09-21 04:14:38 +02:00
}
void manager_check_finished(Manager *m) {
assert(m);
if (m->n_reloading > 0)
return;
/* Verify that we are actually running currently. Initially
* the exit code is set to invalid, and during operation it is
* then set to MANAGER_OK */
if (m->exit_code != MANAGER_OK)
return;
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
(void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
return;
}
manager_flip_auto_status(m, false);
/* Notify Type=idle units that we are done now */
manager_close_idle_pipe(m);
/* Turn off confirm spawn now */
m->confirm_spawn = false;
/* No need to update ask password status when we're going non-interactive */
manager_close_ask_password(m);
/* This is no longer the first boot */
manager_set_first_boot(m, false);
if (dual_timestamp_is_set(&m->finish_timestamp))
return;
dual_timestamp_get(&m->finish_timestamp);
manager_notify_finished(m);
manager_invalidate_startup_units(m);
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
char *p;
int r;
assert(m);
assert(generator);
assert(name);
if (*generator)
return 0;
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
/* systemd --system, not running --test */
p = strappend("/run/systemd/", name);
if (!p)
return log_oom();
r = mkdir_p_label(p, 0755);
if (r < 0) {
log_error_errno(r, "Failed to create generator directory %s: %m", p);
free(p);
return r;
}
} else if (m->running_as == MANAGER_USER) {
const char *s = NULL;
s = getenv("XDG_RUNTIME_DIR");
if (!s)
return -EINVAL;
p = strjoin(s, "/systemd/", name, NULL);
if (!p)
return log_oom();
r = mkdir_p_label(p, 0755);
if (r < 0) {
log_error_errno(r, "Failed to create generator directory %s: %m", p);
free(p);
return r;
}
} else {
/* systemd --system --test */
p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
if (!p)
return log_oom();
if (!mkdtemp(p)) {
2015-10-28 18:23:07 +01:00
log_error_errno(errno, "Failed to create generator directory %s: %m", p);
2013-03-01 18:30:01 +01:00
free(p);
return -errno;
}
}
*generator = p;
return 0;
}
static void trim_generator_dir(Manager *m, char **generator) {
assert(m);
assert(generator);
if (!*generator)
return;
if (rmdir(*generator) >= 0)
*generator = mfree(*generator);
return;
}
static int manager_run_generators(Manager *m) {
core: fix memory leak in manager_run_generators() If systemd is built with GCC address sanitizer or leak sanitizer the following memory leak ocurs: May 12 02:02:46 linux.site systemd[326]: ================================================================= May 12 02:02:46 linux.site systemd[326]: ==326==ERROR: LeakSanitizer: detected memory leaks May 12 02:02:46 linux.site systemd[326]: Direct leak of 101 byte(s) in 3 object(s) allocated from: May 12 02:02:46 linux.site systemd[326]: #0 0x7fd1f504993f in strdup (/usr/lib64/libasan.so.2+0x6293f) May 12 02:02:46 linux.site systemd[326]: #1 0x55d6ffac5336 in strv_new_ap src/shared/strv.c:163 May 12 02:02:46 linux.site systemd[326]: #2 0x55d6ffac56a9 in strv_new src/shared/strv.c:185 May 12 02:02:46 linux.site systemd[326]: #3 0x55d6ffa80272 in generator_paths src/shared/path-lookup.c:223 May 12 02:02:46 linux.site systemd[326]: #4 0x55d6ff9bdb0f in manager_run_generators src/core/manager.c:2828 May 12 02:02:46 linux.site systemd[326]: #5 0x55d6ff9b1a10 in manager_startup src/core/manager.c:1121 May 12 02:02:46 linux.site systemd[326]: #6 0x55d6ff9a78e3 in main src/core/main.c:1667 May 12 02:02:46 linux.site systemd[326]: #7 0x7fd1f394e8c4 in __libc_start_main (/lib64/libc.so.6+0x208c4) May 12 02:02:46 linux.site systemd[326]: Direct leak of 29 byte(s) in 1 object(s) allocated from: May 12 02:02:46 linux.site systemd[326]: #0 0x7fd1f504993f in strdup (/usr/lib64/libasan.so.2+0x6293f) May 12 02:02:46 linux.site systemd[326]: #1 0x55d6ffac5288 in strv_new_ap src/shared/strv.c:152 May 12 02:02:46 linux.site systemd[326]: #2 0x55d6ffac56a9 in strv_new src/shared/strv.c:185 May 12 02:02:46 linux.site systemd[326]: #3 0x55d6ffa80272 in generator_paths src/shared/path-lookup.c:223 May 12 02:02:46 linux.site systemd[326]: #4 0x55d6ff9bdb0f in manager_run_generators src/core/manager.c:2828 May 12 02:02:46 linux.site systemd[326]: #5 0x55d6ff9b1a10 in manager_startup src/core/manager.c:1121 May 12 02:02:46 linux.site systemd[326]: #6 0x55d6ff9a78e3 in main src/core/main.c:1667 May 12 02:02:46 linux.site systemd[326]: #7 0x7fd1f394e8c4 in __libc_start_main (/lib64/libc.so.6+0x208c4) May 12 02:02:46 linux.site systemd[326]: SUMMARY: AddressSanitizer: 130 byte(s) leaked in 4 allocation(s). There is a leak due to the the use of cleanup_free instead _cleanup_strv_free_
2015-05-12 04:30:38 +02:00
_cleanup_strv_free_ char **paths = NULL;
const char *argv[5];
char **path;
int r;
2010-11-11 21:28:33 +01:00
assert(m);
if (m->test_run)
return 0;
paths = generator_paths(m->running_as);
if (!paths)
return log_oom();
2010-11-11 21:28:33 +01:00
/* Optimize by skipping the whole process by not creating output directories
* if no generators are found. */
STRV_FOREACH(path, paths) {
r = access(*path, F_OK);
if (r == 0)
goto found;
if (errno != ENOENT)
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
2010-11-11 21:28:33 +01:00
}
return 0;
2010-11-11 21:28:33 +01:00
found:
r = create_generator_dir(m, &m->generator_unit_path, "generator");
if (r < 0)
goto finish;
r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
if (r < 0)
goto finish;
2010-11-11 21:28:33 +01:00
r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
if (r < 0)
goto finish;
2010-11-11 21:28:33 +01:00
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
argv[1] = m->generator_unit_path;
argv[2] = m->generator_unit_path_early;
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
2010-11-11 21:28:33 +01:00
RUN_WITH_UMASK(0022)
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
2010-11-11 21:28:33 +01:00
finish:
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
trim_generator_dir(m, &m->generator_unit_path_late);
return r;
2010-11-11 21:28:33 +01:00
}
static void remove_generator_dir(Manager *m, char **generator) {
2010-11-11 21:28:33 +01:00
assert(m);
assert(generator);
2010-11-11 21:28:33 +01:00
if (!*generator)
2010-11-11 21:28:33 +01:00
return;
strv_remove(m->lookup_paths.unit_path, *generator);
(void) rm_rf(*generator, REMOVE_ROOT);
2010-11-11 21:28:33 +01:00
*generator = mfree(*generator);
}
static void manager_undo_generators(Manager *m) {
assert(m);
remove_generator_dir(m, &m->generator_unit_path);
remove_generator_dir(m, &m->generator_unit_path_early);
remove_generator_dir(m, &m->generator_unit_path_late);
2010-11-11 21:28:33 +01:00
}
int manager_environment_add(Manager *m, char **minus, char **plus) {
char **a = NULL, **b = NULL, **l;
assert(m);
l = m->environment;
if (!strv_isempty(minus)) {
a = strv_env_delete(l, 1, minus);
if (!a)
return -ENOMEM;
l = a;
}
if (!strv_isempty(plus)) {
b = strv_env_merge(2, l, plus);
if (!b) {
strv_free(a);
return -ENOMEM;
}
l = b;
}
if (m->environment != l)
strv_free(m->environment);
if (a != l)
strv_free(a);
if (b != l)
strv_free(b);
m->environment = l;
manager_clean_environment(m);
strv_sort(m->environment);
return 0;
}
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
int i;
assert(m);
for (i = 0; i < _RLIMIT_MAX; i++) {
if (!default_rlimit[i])
continue;
m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1);
if (!m->rlimit[i])
return -ENOMEM;
}
return 0;
}
void manager_recheck_journal(Manager *m) {
Unit *u;
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
if (u && SOCKET(u)->state != SOCKET_RUNNING) {
log_close_journal();
return;
}
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
if (u && SERVICE(u)->state != SERVICE_RUNNING) {
log_close_journal();
return;
}
/* Hmm, OK, so the socket is fully up and the service is up
* too, then let's make use of the thing. */
log_open();
}
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 != MANAGER_SYSTEM)
return;
if (m->show_status != mode)
log_debug("%s showing of status.",
mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
m->show_status = mode;
if (mode > 0)
(void) touch("/run/systemd/show-status");
else
(void) unlink("/run/systemd/show-status");
}
static bool manager_get_show_status(Manager *m, StatusType type) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return false;
if (m->no_console_output)
return false;
if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
return false;
/* If we cannot find out the status properly, just proceed. */
if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
return false;
if (m->show_status > 0)
return true;
return false;
}
void manager_set_first_boot(Manager *m, bool b) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return;
if (m->first_boot != (int) b) {
if (b)
(void) touch("/run/systemd/first-boot");
else
(void) unlink("/run/systemd/first-boot");
}
m->first_boot = b;
}
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
va_list ap;
/* If m is NULL, assume we're after shutdown and let the messages through. */
if (m && !manager_get_show_status(m, type))
return;
/* XXX We should totally drop the check for ephemeral here
* and thus effectively make 'Type=idle' pointless. */
if (type == STATUS_TYPE_EPHEMERAL && m && m->n_on_console > 0)
return;
va_start(ap, format);
status_vprintf(status, true, type == STATUS_TYPE_EPHEMERAL, format, ap);
va_end(ap);
}
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
char p[strlen(path)+1];
assert(m);
assert(path);
strcpy(p, path);
path_kill_slashes(p);
return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
}
const char *manager_get_runtime_prefix(Manager *m) {
assert(m);
return m->running_as == MANAGER_SYSTEM ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}
int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
unsigned size;
int r;
assert(m);
assert(u->manager == m);
size = set_size(m->failed_units);
if (failed) {
r = set_ensure_allocated(&m->failed_units, NULL);
if (r < 0)
return log_oom();
if (set_put(m->failed_units, u) < 0)
return log_oom();
} else
(void) set_remove(m->failed_units, u);
if (set_size(m->failed_units) != size)
bus_manager_send_change_signal(m);
return 0;
}
ManagerState manager_state(Manager *m) {
Unit *u;
assert(m);
/* Did we ever finish booting? If not then we are still starting up */
if (!dual_timestamp_is_set(&m->finish_timestamp)) {
u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return MANAGER_INITIALIZING;
return MANAGER_STARTING;
}
/* Is the special shutdown target queued? If so, we are in shutdown state */
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))
return MANAGER_STOPPING;
/* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
(u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
(u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
/* Are there any failed units? If so, we are in degraded mode */
if (set_size(m->failed_units) > 0)
return MANAGER_DEGRADED;
return MANAGER_RUNNING;
}
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
[MANAGER_RUNNING] = "running",
[MANAGER_DEGRADED] = "degraded",
[MANAGER_MAINTENANCE] = "maintenance",
[MANAGER_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);