manager.c revision 80fbf05e75b75b7dd342ec844275efae90c479ec
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2010 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <assert.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <errno.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <string.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/epoll.h>
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen#include <signal.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include <sys/signalfd.h>
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering#include <sys/wait.h>
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering#include <unistd.h>
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering#include <sys/poll.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/reboot.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/ioctl.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <linux/kd.h>
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering#include <termios.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <fcntl.h>
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include <sys/types.h>
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include <sys/stat.h>
28b8191e2f391f043d380d47eb79ed9ff66f14bdLennart Poettering#include <dirent.h>
28b8191e2f391f043d380d47eb79ed9ff66f14bdLennart Poettering
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#ifdef HAVE_AUDIT
23502de3b0891455c8ce499a9eb61b69d060a829Daniel Mack#include <libaudit.h>
23502de3b0891455c8ce499a9eb61b69d060a829Daniel Mack#endif
23502de3b0891455c8ce499a9eb61b69d060a829Daniel Mack
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include <systemd/sd-daemon.h>
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "manager.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "transaction.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "hashmap.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "macro.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "strv.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "log.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "util.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "mkdir.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "ratelimit.h"
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering#include "cgroup.h"
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering#include "mount-setup.h"
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering#include "unit-name.h"
e0240c64b76ba8f0c9219feb23a5783f23100216Lennart Poettering#include "dbus-unit.h"
e0240c64b76ba8f0c9219feb23a5783f23100216Lennart Poettering#include "dbus-job.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "missing.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "path-lookup.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "special.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "bus-errors.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "exit-status.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "virt.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "watchdog.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "cgroup-util.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#include "path-util.h"
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering#define GC_QUEUE_ENTRIES_MAX 16
6f717d0817573a76c3e586eae02793d8b23a0581Lennart Poettering
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering/* Where clients shall send notification messages to */
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering
d15ad74251454d55b715958d8e6f50f45195904aLennart Poetteringstatic int manager_setup_notify(Manager *m) {
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering union {
d15ad74251454d55b715958d8e6f50f45195904aLennart Poettering struct sockaddr sa;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering struct sockaddr_un un;
f57e3cd5fa709ec0f52531eccba909ac0851927cLennart Poettering } sa;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering struct epoll_event ev;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering int one = 1, r;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering mode_t u;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering assert(m);
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering m->notify_watch.type = WATCH_NOTIFY;
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering log_error("Failed to allocate notification socket: %m");
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering return -errno;
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering }
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering zero(sa);
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering sa.sa.sa_family = AF_UNIX;
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering if (getpid() != 1)
1b4f6e79ec51a57003896a0b605fba427b4a98d2Lennart Poettering snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering else {
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering unlink(NOTIFY_SOCKET_SYSTEM);
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering }
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering if (sa.un.sun_path[0] == '@')
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering sa.un.sun_path[0] = 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering u = umask(0111);
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
7b50eb2efa122200e39646c19a29abab302f7d24Lennart Poettering umask(u);
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0) {
ee3d6aff9bd73c1b23e29d1fa1fa6f7a1ef0533bLennart Poettering log_error("bind() failed: %m");
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering return -errno;
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering }
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering log_error("SO_PASSCRED failed: %m");
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering return -errno;
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering }
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering zero(ev);
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering ev.events = EPOLLIN;
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering ev.data.ptr = &m->notify_watch;
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -errno;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (sa.un.sun_path[0] == 0)
a43a068a30f7a47aba39f8b48d5db0c4d39fd21dZbigniew Jędrzejewski-Szmek sa.un.sun_path[0] = '@';
d75acfb059ece4512278b8820a9103664996f1e5Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!(m->notify_socket = strdup(sa.un.sun_path)))
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering return -ENOMEM;
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering log_debug("Using notification socket %s", m->notify_socket);
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering return 0;
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int enable_special_signals(Manager *m) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int fd;
8ac4e9e1e54397f6d1745c2a7a806132418c7da2Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(m);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Enable that we get SIGINT on control-alt-del. In containers
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * this will fail with EPERM, so ignore that. */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to enable ctrl-alt-del handling: %m");
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
c0eb11cfd016381fe02875a4ef29c1ade00c94e7Lennart Poettering if (fd < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Support systems without virtual console */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (fd != -ENOENT)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to open /dev/tty0: %m");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering } else {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Enable that we get SIGWINCH on kbrequest */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to enable kbrequest handling: %s", strerror(errno));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering close_nointr_nofail(fd);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering }
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering return 0;
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering}
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poetteringstatic int manager_setup_signals(Manager *m) {
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering sigset_t mask;
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering struct epoll_event ev;
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering struct sigaction sa;
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek assert(m);
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek /* We are not interested in SIGSTOP and friends. */
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek zero(sa);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek sa.sa_handler = SIG_DFL;
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek assert_se(sigemptyset(&mask) == 0);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek sigset_add_many(&mask,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek SIGCHLD, /* Child died */
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek SIGTERM, /* Reexecute daemon */
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek SIGHUP, /* Reload configuration */
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek SIGUSR1, /* systemd/upstart: reconnect to D-Bus */
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering SIGUSR2, /* systemd: dump status */
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen SIGINT, /* Kernel sends us this on control-alt-del */
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering SIGPWR, /* Some kernel drivers and upsd send us this on power failure */
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering SIGRTMIN+0, /* systemd: start default.target */
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen SIGRTMIN+1, /* systemd: isolate rescue.target */
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen SIGRTMIN+2, /* systemd: isolate emergency.target */
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering SIGRTMIN+3, /* systemd: start halt.target */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+4, /* systemd: start poweroff.target */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+5, /* systemd: start reboot.target */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+6, /* systemd: start kexec.target */
f91dc2400dc33e9a0745ecaaef7489af116dca38Lennart Poettering SIGRTMIN+13, /* systemd: Immediate halt */
f91dc2400dc33e9a0745ecaaef7489af116dca38Lennart Poettering SIGRTMIN+14, /* systemd: Immediate poweroff */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+15, /* systemd: Immediate reboot */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+16, /* systemd: Immediate kexec */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+20, /* systemd: enable status messages */
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek SIGRTMIN+21, /* systemd: disable status messages */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+27, /* systemd: set log target to console */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+28, /* systemd: set log target to kmsg */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg */
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek -1);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek m->signal_watch.type = WATCH_SIGNAL;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek return -errno;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek zero(ev);
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen ev.events = EPOLLIN;
9ead3519c54b6d1b79b35541873b5cf7c8b3a7d3Lennart Poettering ev.data.ptr = &m->signal_watch;
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen return -errno;
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen if (m->running_as == MANAGER_SYSTEM)
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering return enable_special_signals(m);
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering return 0;
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering}
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poetteringstatic void manager_strip_environment(Manager *m) {
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering assert(m);
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering
6af47493de0ef2b66d4c3fbcdd4a2e12fec4bfbaLennart Poettering /* Remove variables from the inherited set that are part of
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen * the container interface:
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen strv_remove_prefix(m->environment, "container=");
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen strv_remove_prefix(m->environment, "container_");
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen /* Remove variables from the inherited set that are part of
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen * the initrd interface:
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen strv_remove_prefix(m->environment, "RD_");
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen}
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmekint manager_new(ManagerRunningAs running_as, Manager **_m) {
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek Manager *m;
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek int r = -ENOMEM;
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek assert(_m);
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek assert(running_as >= 0);
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek assert(running_as < _MANAGER_RUNNING_AS_MAX);
48d45d2b49d2adb870cd5f1bc7cb389b33655f1cZbigniew Jędrzejewski-Szmek
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!(m = new0(Manager, 1)))
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -ENOMEM;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering dual_timestamp_get(&m->startup_timestamp);
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering m->running_as = running_as;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering m->exit_code = _MANAGER_EXIT_CODE_INVALID;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering m->pin_cgroupfs_fd = -1;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering m->idle_pipe[0] = m->idle_pipe[1] = -1;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering#ifdef HAVE_AUDIT
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering m->audit_fd = -1;
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering#endif
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering m->environment = strv_copy(environ);
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering if (!m->environment)
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering goto fail;
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering manager_strip_environment(m);
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering if (running_as == MANAGER_SYSTEM) {
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering m->default_controllers = strv_new("cpu", NULL);
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering if (!m->default_controllers)
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering goto fail;
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering }
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering goto fail;
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering
85aeaccc10b111e8d16d3879b7c30a219ee6e10aLennart Poettering if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering goto fail;
36d9205d669bcdcb04fa730d1f3549a9fc9a9001Tom Gundersen
801ad6a6a9cd8fbd58b9f9c27f20dbb3c87d47ddLennart Poettering if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering goto fail;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
28b9b7640603f88cb49f95609331fa5072715f15Lennart Poettering goto fail;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
105e151299dc1208855380be2b22d0db2d66ebc6Lennart Poettering if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
5d27351f8546530cf779847b0b04b0172c09f9d0Tom Gundersen goto fail;
547973dea7abd6c124ff6c79fe2bbe322a7314aeLennart Poettering
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering goto fail;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
7778dffff3d8bd7438fe19a248c16203668324c9Daniel Mack if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
7778dffff3d8bd7438fe19a248c16203668324c9Daniel Mack goto fail;
7778dffff3d8bd7438fe19a248c16203668324c9Daniel Mack
7778dffff3d8bd7438fe19a248c16203668324c9Daniel Mack if ((r = manager_setup_signals(m)) < 0)
f57e3cd5fa709ec0f52531eccba909ac0851927cLennart Poettering goto fail;
f57e3cd5fa709ec0f52531eccba909ac0851927cLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering if ((r = manager_setup_cgroup(m)) < 0)
8bf52d3d17d364438191077d0750b8b80b5dc53aLennart Poettering goto fail;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if ((r = manager_setup_notify(m)) < 0)
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering goto fail;
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering /* Try to connect to the busses, if possible. */
7b50eb2efa122200e39646c19a29abab302f7d24Lennart Poettering if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering goto fail;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering#ifdef HAVE_AUDIT
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering if ((m->audit_fd = audit_open()) < 0 &&
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering /* If the kernel lacks netlink or audit support,
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering * don't worry about it. */
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
ab481675f98d3d3f12e7e48ba6d2159123b9c7bfLennart Poettering log_error("Failed to connect to audit log: %m");
97c67192eadaffe67b803ec5b991a92bb1137d0bLennart Poettering#endif
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering m->taint_usr = dir_is_empty("/usr") > 0;
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt *_m = m;
c9c72065419e6595131a6fe1e663e2184a843f7cLennart Poettering return 0;
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poetteringfail:
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering manager_free(m);
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering return r;
8e54f5d90a6b9dd1ff672fb97ea98de66c49e332Lennart Poettering}
8730bccfc59fe507bd3e0a3abcf411b497ac4f0eLennart Poettering
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
Unit *u;
unsigned n = 0;
assert(m);
while ((u = m->cleanup_queue)) {
assert(u->in_cleanup_queue);
unit_free(u);
n++;
}
return n;
}
enum {
GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */
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;
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;
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;
if (other->gc_marker != gc_marker + GC_OFFSET_BAD)
is_bad = false;
}
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;
unit_add_to_gc_queue(u);
return;
bad:
/* 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;
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;
unsigned gc_marker;
assert(m);
if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) &&
(m->gc_queue_timestamp <= 0 ||
(m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC)))
return 0;
log_debug("Running GC...");
m->gc_marker += _GC_OFFSET_MAX;
if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
m->gc_marker = 1;
gc_marker = m->gc_marker;
while ((u = m->gc_queue)) {
assert(u->in_gc_queue);
unit_gc_sweep(u, gc_marker);
LIST_REMOVE(Unit, 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) {
log_debug("Collecting %s", u->id);
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
}
}
m->n_in_gc_queue = 0;
m->gc_queue_timestamp = 0;
return n;
}
static void manager_clear_jobs_and_units(Manager *m) {
Unit *u;
assert(m);
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));
}
void manager_free(Manager *m) {
UnitType c;
assert(m);
manager_clear_jobs_and_units(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
/* If we reexecute ourselves, we keep the root cgroup
* around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
manager_undo_generators(m);
bus_done(m);
hashmap_free(m->units);
hashmap_free(m->jobs);
hashmap_free(m->watch_pids);
hashmap_free(m->watch_bus);
if (m->epoll_fd >= 0)
close_nointr_nofail(m->epoll_fd);
if (m->signal_watch.fd >= 0)
close_nointr_nofail(m->signal_watch.fd);
if (m->notify_watch.fd >= 0)
close_nointr_nofail(m->notify_watch.fd);
#ifdef HAVE_AUDIT
if (m->audit_fd >= 0)
audit_close(m->audit_fd);
#endif
free(m->notify_socket);
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
strv_free(m->default_controllers);
hashmap_free(m->cgroup_bondings);
set_free_free(m->unit_path_cache);
close_pipe(m->idle_pipe);
free(m->switch_root);
free(m->switch_root_init);
free(m);
}
int manager_enumerate(Manager *m) {
int r = 0, q;
UnitType c;
assert(m);
/* 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_vtable[c]->enumerate)
if ((q = unit_vtable[c]->enumerate(m)) < 0)
r = q;
manager_dispatch_load_queue(m);
return r;
}
int manager_coldplug(Manager *m) {
int r = 0, q;
Iterator i;
Unit *u;
char *k;
assert(m);
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
/* ignore aliases */
if (u->id != k)
continue;
if ((q = unit_coldplug(u)) < 0)
r = q;
}
return r;
}
static void manager_build_unit_path_cache(Manager *m) {
char **i;
DIR *d = NULL;
int r;
assert(m);
set_free_free(m->unit_path_cache);
if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) {
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;
if (!(d = opendir(*i))) {
log_error("Failed to open directory: %m");
continue;
}
while ((de = readdir(d))) {
char *p;
if (ignore_file(de->d_name))
continue;
p = join(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
if (!p) {
r = -ENOMEM;
goto fail;
}
if ((r = set_put(m->unit_path_cache, p)) < 0) {
free(p);
goto fail;
}
}
closedir(d);
d = NULL;
}
return;
fail:
log_error("Failed to build unit path cache: %s", strerror(-r));
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
if (d)
closedir(d);
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r, q;
assert(m);
manager_run_generators(m);
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 ++;
/* First, enumerate what we can from all config files */
r = manager_enumerate(m);
/* Second, deserialize if there is something to deserialize */
if (serialization)
if ((q = manager_deserialize(m, serialization, fds)) < 0)
r = q;
/* Third, fire things up! */
if ((q = manager_coldplug(m)) < 0)
r = q;
if (serialization) {
assert(m->n_reloading > 0);
m->n_reloading --;
}
return r;
}
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, DBusError *e, Job **_ret) {
int r;
Transaction *tr;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(unit);
assert(mode < _JOB_MODE_MAX);
if (mode == JOB_ISOLATE && type != JOB_START) {
dbus_set_error(e, BUS_ERROR_INVALID_JOB_MODE, "Isolate is only valid for start.");
return -EINVAL;
}
if (mode == JOB_ISOLATE && !unit->allow_isolate) {
dbus_set_error(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
return -EPERM;
}
log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
job_type_collapse(&type, unit);
tr = transaction_new();
if (!tr)
return -ENOMEM;
r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, 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;
log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) tr->anchor_job->id);
if (_ret)
*_ret = tr->anchor_job;
transaction_free(tr);
return 0;
tr_abort:
transaction_abort(tr);
transaction_free(tr);
return r;
}
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, DBusError *e, Job **_ret) {
Unit *unit;
int r;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(name);
assert(mode < _JOB_MODE_MAX);
if ((r = manager_load_unit(m, name, NULL, NULL, &unit)) < 0)
return r;
return manager_add_job(m, type, unit, mode, override, e, _ret);
}
Job *manager_get_job(Manager *m, uint32_t id) {
assert(m);
return hashmap_get(m->jobs, UINT32_TO_PTR(id));
}
Unit *manager_get_unit(Manager *m, const char *name) {
assert(m);
assert(name);
return hashmap_get(m->units, name);
}
unsigned manager_dispatch_load_queue(Manager *m) {
Unit *u;
unsigned n = 0;
assert(m);
/* Make sure we are not run recursively */
if (m->dispatching_load_queue)
return 0;
m->dispatching_load_queue = true;
/* Dispatches the load queue. Takes a unit from the queue and
* tries to load its data until the queue is empty */
while ((u = m->load_queue)) {
assert(u->in_load_queue);
unit_load(u);
n++;
}
m->dispatching_load_queue = false;
return n;
}
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
Unit *ret;
UnitType t;
int r;
assert(m);
assert(name || path);
/* This will prepare the unit for loading, but not actually
* load anything from disk. */
if (path && !is_path(path)) {
dbus_set_error(e, BUS_ERROR_INVALID_PATH, "Path %s is not absolute.", path);
return -EINVAL;
}
if (!name)
name = path_get_file_name(path);
t = unit_name_to_type(name);
if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid_no_type(name, false)) {
dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name);
return -EINVAL;
}
ret = manager_get_unit(m, name);
if (ret) {
*_ret = ret;
return 1;
}
ret = unit_new(m, unit_vtable[t]->object_size);
if (!ret)
return -ENOMEM;
if (path) {
ret->fragment_path = strdup(path);
if (!ret->fragment_path) {
unit_free(ret);
return -ENOMEM;
}
}
if ((r = unit_add_name(ret, name)) < 0) {
unit_free(ret);
return r;
}
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, DBusError *e, Unit **_ret) {
int r;
assert(m);
/* This will load the service information files, but not actually
* start any services or anything. */
if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0)
return r;
manager_dispatch_load_queue(m);
if (_ret)
*_ret = unit_follow_merge(*_ret);
return 0;
}
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
Iterator i;
Job *j;
assert(s);
assert(f);
HASHMAP_FOREACH(j, s->jobs, i)
job_dump(j, f, prefix);
}
void manager_dump_units(Manager *s, FILE *f, const char *prefix) {
Iterator i;
Unit *u;
const char *t;
assert(s);
assert(f);
HASHMAP_FOREACH_KEY(u, t, s->units, i)
if (u->id == t)
unit_dump(u, f, prefix);
}
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);
}
unsigned manager_dispatch_run_queue(Manager *m) {
Job *j;
unsigned n = 0;
if (m->dispatching_run_queue)
return 0;
m->dispatching_run_queue = true;
while ((j = m->run_queue)) {
assert(j->installed);
assert(j->in_run_queue);
job_run_and_invalidate(j);
n++;
}
m->dispatching_run_queue = false;
return n;
}
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;
return n;
}
static int manager_process_notify_fd(Manager *m) {
ssize_t n;
assert(m);
for (;;) {
char buf[4096];
struct msghdr msghdr;
struct iovec iovec;
struct ucred *ucred;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
} control;
Unit *u;
char **tags;
zero(iovec);
iovec.iov_base = buf;
iovec.iov_len = sizeof(buf)-1;
zero(control);
zero(msghdr);
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) {
if (n >= 0)
return -EIO;
if (errno == EAGAIN || errno == EINTR)
break;
return -errno;
}
if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
control.cmsghdr.cmsg_level != SOL_SOCKET ||
control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
log_warning("Received notify message without credentials. Ignoring.");
continue;
}
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid))))
if (!(u = cgroup_unit_by_pid(m, ucred->pid))) {
log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
continue;
}
assert((size_t) n < sizeof(buf));
buf[n] = 0;
if (!(tags = strv_split(buf, "\n\r")))
return -ENOMEM;
log_debug("Got notification message for unit %s", u->id);
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
strv_free(tags);
}
return 0;
}
static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
siginfo_t si;
Unit *u;
int r;
zero(si);
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
* it while it is a zombie. */
if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
if (errno == ECHILD)
break;
if (errno == EINTR)
continue;
return -errno;
}
if (si.si_pid <= 0)
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
char *name = NULL;
get_process_comm(si.si_pid, &name);
log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name));
free(name);
}
/* Let's flush any message the dying child might still
* have queued for us. This ensures that the process
* still exists in /proc so that we can figure out
* which cgroup and hence unit it belongs to. */
if ((r = manager_process_notify_fd(m)) < 0)
return r;
/* And now figure out the unit this belongs to */
if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid))))
u = cgroup_unit_by_pid(m, si.si_pid);
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
if (errno == EINTR)
continue;
return -errno;
}
if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED)
continue;
log_debug("Child %lu died (code=%s, status=%i/%s)",
(long unsigned) si.si_pid,
sigchld_code_to_string(si.si_code),
si.si_status,
strna(si.si_code == CLD_EXITED
? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
: signal_to_string(si.si_status)));
if (!u)
continue;
log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->id);
hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid));
UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
}
return 0;
}
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
int r;
DBusError error;
dbus_error_init(&error);
log_debug("Activating special unit %s", name);
if ((r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL)) < 0)
log_error("Failed to enqueue %s job: %s", name, bus_error(&error, r));
dbus_error_free(&error);
return r;
}
static int manager_process_signal_fd(Manager *m) {
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
assert(m);
for (;;) {
if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
if (n >= 0)
return -EIO;
if (errno == EINTR || errno == EAGAIN)
break;
return -errno;
}
if (sfsi.ssi_pid > 0) {
char *p = NULL;
get_process_comm(sfsi.ssi_pid, &p);
log_debug("Received SIG%s from PID %lu (%s).",
signal_to_string(sfsi.ssi_signo),
(unsigned long) sfsi.ssi_pid, strna(p));
free(p);
} else
log_debug("Received SIG%s.", signal_to_string(sfsi.ssi_signo));
switch (sfsi.ssi_signo) {
case SIGCHLD:
sigchld = true;
break;
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) {
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
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;
case SIGUSR1: {
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);
}
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);
}
break;
}
case SIGUSR2: {
FILE *f;
char *dump = NULL;
size_t size;
if (!(f = open_memstream(&dump, &size))) {
log_warning("Failed to allocate memory stream.");
break;
}
manager_dump_units(m, f, "\t");
manager_dump_jobs(m, f, "\t");
if (ferror(f)) {
fclose(f);
free(dump);
log_warning("Failed to write status stream");
break;
}
fclose(f);
log_dump(LOG_INFO, dump);
free(dump);
break;
}
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,
[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:
log_debug("Enabling showing of status.");
manager_set_show_status(m, true);
break;
case 21:
log_debug("Disabling showing of status.");
manager_set_show_status(m, false);
break;
case 22:
log_set_max_level(LOG_DEBUG);
log_notice("Setting log level to debug.");
break;
case 23:
log_set_max_level(LOG_INFO);
log_notice("Setting log level to info.");
break;
case 26:
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;
case 29:
log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
log_notice("Setting log target to syslog-or-kmsg.");
break;
default:
log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
}
}
}
}
if (sigchld)
return manager_dispatch_sigchld(m);
return 0;
}
static int process_event(Manager *m, struct epoll_event *ev) {
int r;
Watch *w;
assert(m);
assert(ev);
assert_se(w = ev->data.ptr);
if (w->type == WATCH_INVALID)
return 0;
switch (w->type) {
case WATCH_SIGNAL:
/* An incoming signal? */
if (ev->events != EPOLLIN)
return -EINVAL;
if ((r = manager_process_signal_fd(m)) < 0)
return r;
break;
case WATCH_NOTIFY:
/* An incoming daemon notification event? */
if (ev->events != EPOLLIN)
return -EINVAL;
if ((r = manager_process_notify_fd(m)) < 0)
return r;
break;
case WATCH_FD:
/* Some fd event, to be dispatched to the units */
UNIT_VTABLE(w->data.unit)->fd_event(w->data.unit, w->fd, ev->events, w);
break;
case WATCH_UNIT_TIMER:
case WATCH_JOB_TIMER: {
uint64_t v;
ssize_t k;
/* Some timer event, to be dispatched to the units */
if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) {
if (k < 0 && (errno == EINTR || errno == EAGAIN))
break;
return k < 0 ? -errno : -EIO;
}
if (w->type == WATCH_UNIT_TIMER)
UNIT_VTABLE(w->data.unit)->timer_event(w->data.unit, v, w);
else
job_timer_event(w->data.job, v, w);
break;
}
case WATCH_MOUNT:
/* Some mount table change, intended for the mount subsystem */
mount_fd_event(m, ev->events);
break;
case WATCH_SWAP:
/* Some swap table change, intended for the swap subsystem */
swap_fd_event(m, ev->events);
break;
case WATCH_UDEV:
/* Some notification from udev, intended for the device subsystem */
device_fd_event(m, ev->events);
break;
case WATCH_DBUS_WATCH:
bus_watch_event(m, w, ev->events);
break;
case WATCH_DBUS_TIMEOUT:
bus_timeout_event(m, w, ev->events);
break;
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
}
return 0;
}
int manager_loop(Manager *m) {
int r;
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
assert(m);
m->exit_code = MANAGER_RUNNING;
/* Release the path cache */
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
manager_check_finished(m);
/* There might still be some zombies hanging around from
* before we were exec()'ed. Leat's reap them */
r = manager_dispatch_sigchld(m);
if (r < 0)
return r;
while (m->exit_code == MANAGER_RUNNING) {
struct epoll_event event;
int n;
int wait_msec = -1;
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);
continue;
}
if (manager_dispatch_load_queue(m) > 0)
continue;
if (manager_dispatch_run_queue(m) > 0)
continue;
if (bus_dispatch(m) > 0)
continue;
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
if (manager_dispatch_gc_queue(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
continue;
if (swap_dispatch_reload(m) > 0)
continue;
/* Sleep for half the watchdog time */
if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC);
if (wait_msec <= 0)
wait_msec = 1;
} else
wait_msec = -1;
n = epoll_wait(m->epoll_fd, &event, 1, wait_msec);
if (n < 0) {
if (errno == EINTR)
continue;
return -errno;
} else if (n == 0)
continue;
assert(n == 1);
r = process_event(m, &event);
if (r < 0)
return r;
}
return m->exit_code;
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u) {
char *n;
Unit *u;
int r;
assert(m);
assert(s);
assert(_u);
if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
return -EINVAL;
n = bus_path_unescape(s+31);
if (!n)
return -ENOMEM;
r = manager_load_unit(m, n, NULL, e, &u);
free(n);
if (r < 0)
return r;
*_u = u;
return 0;
}
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
Job *j;
unsigned id;
int r;
assert(m);
assert(s);
assert(_j);
if (!startswith(s, "/org/freedesktop/systemd1/job/"))
return -EINVAL;
if ((r = safe_atou(s + 30, &id)) < 0)
return r;
if (!(j = manager_get_job(m, id)))
return -ENOENT;
*_j = j;
return 0;
}
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
char *p;
if (m->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 (m->running_as != MANAGER_SYSTEM)
return;
if (u->type != UNIT_SERVICE)
return;
if (!(p = unit_name_to_prefix_and_instance(u->id))) {
log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM));
return;
}
if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
if (errno == EPERM) {
/* We aren't allowed to send audit messages?
* Then let's not retry again. */
audit_close(m->audit_fd);
m->audit_fd = -1;
} else
log_warning("Failed to send audit message: %m");
}
free(p);
#endif
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
int fd = -1;
union sockaddr_union sa;
int n = 0;
char *message = NULL;
/* 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 (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 */
if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
log_error("socket() failed: %m");
return;
}
zero(sa);
sa.sa.sa_family = AF_UNIX;
strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
if (errno != EPIPE &&
errno != EAGAIN &&
errno != ENOENT &&
errno != ECONNREFUSED &&
errno != ECONNRESET &&
errno != ECONNABORTED)
log_error("connect() failed: %m");
goto finish;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
log_error("Out of memory");
goto finish;
}
errno = 0;
if (write(fd, message, n + 1) != n + 1) {
if (errno != EPIPE &&
errno != EAGAIN &&
errno != ENOENT &&
errno != ECONNREFUSED &&
errno != ECONNRESET &&
errno != ECONNABORTED)
log_error("Failed to write Plymouth message: %m");
goto finish;
}
finish:
if (fd >= 0)
close_nointr_nofail(fd);
free(message);
}
void manager_dispatch_bus_name_owner_changed(
Manager *m,
const char *name,
const char* old_owner,
const char *new_owner) {
Unit *u;
assert(m);
assert(name);
if (!(u = hashmap_get(m->watch_bus, name)))
return;
UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
}
void manager_dispatch_bus_query_pid_done(
Manager *m,
const char *name,
pid_t pid) {
Unit *u;
assert(m);
assert(name);
assert(pid >= 1);
if (!(u = hashmap_get(m->watch_bus, name)))
return;
UNIT_VTABLE(u)->bus_query_pid_done(u, name, pid);
}
int manager_open_serialization(Manager *m, FILE **_f) {
char *path = NULL;
mode_t saved_umask;
int fd;
FILE *f;
assert(_f);
if (m->running_as == MANAGER_SYSTEM)
asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid());
else
asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid());
if (!path)
return -ENOMEM;
saved_umask = umask(0077);
fd = mkostemp(path, O_RDWR|O_CLOEXEC);
umask(saved_umask);
if (fd < 0) {
free(path);
return -errno;
}
unlink(path);
log_debug("Serializing state to %s", path);
free(path);
if (!(f = fdopen(fd, "w+")))
return -errno;
*_f = f;
return 0;
}
int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
Iterator i;
Unit *u;
const char *t;
int r;
assert(m);
assert(f);
assert(fds);
m->n_reloading ++;
fprintf(f, "current-job-id=%i\n", m->current_job_id);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp);
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
if (u->id != t)
continue;
if (!unit_can_serialize(u))
continue;
/* Start marker */
fputs(u->id, f);
fputc('\n', f);
if ((r = unit_serialize(u, f, fds)) < 0) {
m->n_reloading --;
return r;
}
}
assert(m->n_reloading > 0);
m->n_reloading --;
if (ferror(f))
return -EIO;
r = bus_fdset_add_all(m, fds);
if (r < 0)
return r;
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 ++;
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, "taint-usr=")) {
int b;
if ((b = parse_boolean(l+10)) < 0)
log_debug("Failed to parse taint /usr flag %s", l+10);
else
m->taint_usr = m->taint_usr || b;
} else if (startswith(l, "initrd-timestamp="))
dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
else if (startswith(l, "startup-timestamp="))
dual_timestamp_deserialize(l+18, &m->startup_timestamp);
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
else
log_debug("Unknown serialization item '%s'", l);
}
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;
goto finish;
}
char_array_0(name);
if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0)
goto finish;
if ((r = unit_deserialize(u, f, fds)) < 0)
goto finish;
}
finish:
if (ferror(f)) {
r = -EIO;
goto finish;
}
assert(m->n_reloading > 0);
m->n_reloading --;
return r;
}
int manager_reload(Manager *m) {
int r, q;
FILE *f;
FDSet *fds;
assert(m);
if ((r = manager_open_serialization(m, &f)) < 0)
return r;
m->n_reloading ++;
if (!(fds = fdset_new())) {
m->n_reloading --;
r = -ENOMEM;
goto finish;
}
if ((r = manager_serialize(m, f, fds)) < 0) {
m->n_reloading --;
goto finish;
}
if (fseeko(f, 0, SEEK_SET) < 0) {
m->n_reloading --;
r = -errno;
goto finish;
}
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
manager_undo_generators(m);
/* Find new unit paths */
lookup_paths_free(&m->lookup_paths);
if ((q = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
r = q;
manager_run_generators(m);
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
if ((q = manager_enumerate(m)) < 0)
r = q;
/* Second, deserialize our stored data */
if ((q = manager_deserialize(m, f, fds)) < 0)
r = q;
fclose(f);
f = NULL;
/* Third, fire things up! */
if ((q = manager_coldplug(m)) < 0)
r = q;
assert(m->n_reloading > 0);
m->n_reloading--;
finish:
if (f)
fclose(f);
if (fds)
fdset_free(fds);
return r;
}
bool manager_is_booting_or_shutting_down(Manager *m) {
Unit *u;
assert(m);
/* Is the initial job still around? */
if (manager_get_job(m, m->default_unit_job_id))
return true;
/* Is there a job for the shutdown target? */
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
if (u)
return !!u->job;
return false;
}
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_pending_inactive(Manager *m, const char *name) {
Unit *u;
assert(m);
assert(name);
/* Returns true if the unit is inactive or going down */
if (!(u = manager_get_unit(m, name)))
return true;
return unit_pending_inactive(u);
}
void manager_check_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t kernel_usec, initrd_usec, userspace_usec, total_usec;
assert(m);
if (hashmap_size(m->jobs) > 0)
return;
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
if (dual_timestamp_is_set(&m->finish_timestamp))
return;
dual_timestamp_get(&m->finish_timestamp);
if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
total_usec = m->finish_timestamp.monotonic;
if (dual_timestamp_is_set(&m->initrd_timestamp)) {
kernel_usec = m->initrd_timestamp.monotonic;
initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic;
log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
format_timespan(kernel, sizeof(kernel), kernel_usec),
format_timespan(initrd, sizeof(initrd), initrd_usec),
format_timespan(userspace, sizeof(userspace), userspace_usec),
format_timespan(sum, sizeof(sum), total_usec));
} else {
kernel_usec = m->startup_timestamp.monotonic;
initrd_usec = 0;
log_info("Startup finished in %s (kernel) + %s (userspace) = %s.",
format_timespan(kernel, sizeof(kernel), kernel_usec),
format_timespan(userspace, sizeof(userspace), userspace_usec),
format_timespan(sum, sizeof(sum), total_usec));
}
} else {
userspace_usec = initrd_usec = kernel_usec = 0;
total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
log_debug("Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec));
}
bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
"READY=1\nSTATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec));
}
void manager_run_generators(Manager *m) {
DIR *d = NULL;
const char *generator_path;
const char *argv[3];
mode_t u;
assert(m);
generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
if (!(d = opendir(generator_path))) {
if (errno == ENOENT)
return;
log_error("Failed to enumerate generator directory: %m");
return;
}
if (!m->generator_unit_path) {
const char *p;
char user_path[] = "/tmp/systemd-generator-XXXXXX";
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
p = "/run/systemd/generator";
if (mkdir_p(p, 0755) < 0) {
log_error("Failed to create generator directory: %m");
goto finish;
}
} else {
if (!(p = mkdtemp(user_path))) {
log_error("Failed to create generator directory: %m");
goto finish;
}
}
if (!(m->generator_unit_path = strdup(p))) {
log_error("Failed to allocate generator unit path.");
goto finish;
}
}
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
argv[1] = m->generator_unit_path;
argv[2] = NULL;
u = umask(0022);
execute_directory(generator_path, d, (char**) argv);
umask(u);
if (rmdir(m->generator_unit_path) >= 0) {
/* Uh? we were able to remove this dir? I guess that
* means the directory was empty, hence let's shortcut
* this */
free(m->generator_unit_path);
m->generator_unit_path = NULL;
goto finish;
}
if (!strv_find(m->lookup_paths.unit_path, m->generator_unit_path)) {
char **l;
if (!(l = strv_append(m->lookup_paths.unit_path, m->generator_unit_path))) {
log_error("Failed to add generator directory to unit search path: %m");
goto finish;
}
strv_free(m->lookup_paths.unit_path);
m->lookup_paths.unit_path = l;
log_debug("Added generator unit path %s to search path.", m->generator_unit_path);
}
finish:
if (d)
closedir(d);
}
void manager_undo_generators(Manager *m) {
assert(m);
if (!m->generator_unit_path)
return;
strv_remove(m->lookup_paths.unit_path, m->generator_unit_path);
rm_rf(m->generator_unit_path, false, true, false);
free(m->generator_unit_path);
m->generator_unit_path = NULL;
}
int manager_set_default_controllers(Manager *m, char **controllers) {
char **l;
assert(m);
l = strv_copy(controllers);
if (!l)
return -ENOMEM;
strv_free(m->default_controllers);
m->default_controllers = l;
cg_shorten_controllers(m->default_controllers);
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, bool b) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return;
m->show_status = b;
if (b)
touch("/run/systemd/show-status");
else
unlink("/run/systemd/show-status");
}
bool manager_get_show_status(Manager *m) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return false;
if (m->show_status)
return true;
/* If Plymouth is running make sure we show the status, so
* that there's something nice to see when people press Esc */
return plymouth_running();
}
static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_SYSTEM] = "system",
[MANAGER_USER] = "user"
};
DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs);