[systemd-commits] Makefile.am po/POTFILES.skip src/ask-password-api.h src/automount.c src/automount.h src/build.h src/bus-errors.h src/cgroup-attr.c src/cgroup-attr.h src/cgroup.c src/cgroup.h src/condition.c src/condition.h src/core src/dbus-automount.c src/dbus-automount.h src/dbus.c src/dbus-device.c src/dbus-device.h src/dbus-execute.c src/dbus-execute.h src/dbus.h src/dbus-job.c src/dbus-job.h src/dbus-loop.h src/dbus-manager.c src/dbus-manager.h src/dbus-mount.c src/dbus-mount.h src/dbus-path.c src/dbus-path.h src/dbus-service.c src/dbus-service.h src/dbus-snapshot.c src/dbus-snapshot.h src/dbus-socket.c src/dbus-socket.h src/dbus-swap.c src/dbus-swap.h src/dbus-target.c src/dbus-target.h src/dbus-timer.c src/dbus-timer.h src/dbus-unit.c src/dbus-unit.h src/device.c src/device.h src/execute.c src/execute.h src/fdset.c src/fdset.h src/ima-setup.c src/ima-setup.h src/initreq.h src/job.c src/job.h src/kmod-setup.c src/kmod-setup.h src/load-dropin.c src/load-dropin.h src/load-fragme nt.c src/load-fragment.h src/locale-setup.c src/locale-setup.h src/manager.c src/manager.h src/mount.c src/mount.h src/namespace.c src/namespace.h src/path.c src/path.h src/polkit.h src/securebits.h src/selinux-setup.c src/selinux-setup.h src/service.c src/service.h src/snapshot.c src/snapshot.h src/socket.c src/socket.h src/spawn-agent.h src/special.h src/swap.c src/swap.h src/sysfs-show.h src/target.c src/target.h src/tcpwrap.c src/tcpwrap.h src/timer.c src/timer.h src/unit.c src/unit.h
Kay Sievers
kay at kemper.freedesktop.org
Wed Apr 11 07:10:43 PDT 2012
Makefile.am | 239 +-
po/POTFILES.skip | 26
src/ask-password-api.h | 33
src/automount.c | 888 ----------
src/automount.h | 76
src/build.h | 69
src/bus-errors.h | 58
src/cgroup-attr.c | 102 -
src/cgroup-attr.h | 49
src/cgroup.c | 556 ------
src/cgroup.h | 94 -
src/condition.c | 323 ---
src/condition.h | 69
src/core/ask-password-api.h | 33
src/core/automount.c | 888 ++++++++++
src/core/automount.h | 76
src/core/build.h | 69
src/core/bus-errors.h | 58
src/core/cgroup-attr.c | 102 +
src/core/cgroup-attr.h | 49
src/core/cgroup.c | 556 ++++++
src/core/cgroup.h | 94 +
src/core/condition.c | 323 +++
src/core/condition.h | 69
src/core/dbus-automount.c | 72
src/core/dbus-automount.h | 34
src/core/dbus-device.c | 65
src/core/dbus-device.h | 34
src/core/dbus-execute.c | 422 ++++
src/core/dbus-execute.h | 125 +
src/core/dbus-job.c | 354 ++++
src/core/dbus-job.h | 36
src/core/dbus-loop.h | 30
src/core/dbus-manager.c | 1544 +++++++++++++++++
src/core/dbus-manager.h | 31
src/core/dbus-mount.c | 168 +
src/core/dbus-mount.h | 34
src/core/dbus-path.c | 119 +
src/core/dbus-path.h | 35
src/core/dbus-service.c | 169 +
src/core/dbus-service.h | 34
src/core/dbus-snapshot.c | 93 +
src/core/dbus-snapshot.h | 33
src/core/dbus-socket.c | 139 +
src/core/dbus-socket.h | 34
src/core/dbus-swap.c | 111 +
src/core/dbus-swap.h | 35
src/core/dbus-target.c | 55
src/core/dbus-target.h | 33
src/core/dbus-timer.c | 137 +
src/core/dbus-timer.h | 34
src/core/dbus-unit.c | 850 +++++++++
src/core/dbus-unit.h | 139 +
src/core/dbus.c | 1483 +++++++++++++++++
src/core/dbus.h | 53
src/core/device.c | 616 +++++++
src/core/device.h | 59
src/core/execute.c | 2113 ++++++++++++++++++++++++
src/core/execute.h | 233 ++
src/core/fdset.c | 167 +
src/core/fdset.h | 40
src/core/ima-setup.c | 115 +
src/core/ima-setup.h | 29
src/core/initreq.h | 77
src/core/job.c | 701 ++++++++
src/core/job.h | 200 ++
src/core/kmod-setup.c | 96 +
src/core/kmod-setup.h | 27
src/core/load-dropin.c | 150 +
src/core/load-dropin.h | 31
src/core/load-fragment.c | 2445 ++++++++++++++++++++++++++++
src/core/load-fragment.h | 91 +
src/core/locale-setup.c | 251 ++
src/core/locale-setup.h | 27
src/core/manager.c | 3235 +++++++++++++++++++++++++++++++++++++
src/core/manager.h | 304 +++
src/core/mount.c | 1930 ++++++++++++++++++++++
src/core/mount.h | 124 +
src/core/namespace.c | 346 ++++
src/core/namespace.h | 34
src/core/path.c | 771 ++++++++
src/core/path.h | 113 +
src/core/polkit.h | 36
src/core/securebits.h | 45
src/core/selinux-setup.c | 112 +
src/core/selinux-setup.h | 29
src/core/service.c | 3797 ++++++++++++++++++++++++++++++++++++++++++++
src/core/service.h | 220 ++
src/core/snapshot.c | 309 +++
src/core/snapshot.h | 53
src/core/socket.c | 2218 +++++++++++++++++++++++++
src/core/socket.h | 173 ++
src/core/spawn-agent.h | 28
src/core/special.h | 88 +
src/core/swap.c | 1415 ++++++++++++++++
src/core/swap.h | 128 +
src/core/sysfs-show.h | 27
src/core/target.c | 224 ++
src/core/target.h | 47
src/core/tcpwrap.c | 68
src/core/tcpwrap.h | 29
src/core/timer.c | 520 ++++++
src/core/timer.h | 93 +
src/core/unit.c | 2676 +++++++++++++++++++++++++++++++
src/core/unit.h | 562 ++++++
src/dbus-automount.c | 72
src/dbus-automount.h | 34
src/dbus-device.c | 65
src/dbus-device.h | 34
src/dbus-execute.c | 422 ----
src/dbus-execute.h | 125 -
src/dbus-job.c | 354 ----
src/dbus-job.h | 36
src/dbus-loop.h | 30
src/dbus-manager.c | 1544 -----------------
src/dbus-manager.h | 31
src/dbus-mount.c | 168 -
src/dbus-mount.h | 34
src/dbus-path.c | 119 -
src/dbus-path.h | 35
src/dbus-service.c | 169 -
src/dbus-service.h | 34
src/dbus-snapshot.c | 93 -
src/dbus-snapshot.h | 33
src/dbus-socket.c | 139 -
src/dbus-socket.h | 34
src/dbus-swap.c | 111 -
src/dbus-swap.h | 35
src/dbus-target.c | 55
src/dbus-target.h | 33
src/dbus-timer.c | 137 -
src/dbus-timer.h | 34
src/dbus-unit.c | 850 ---------
src/dbus-unit.h | 139 -
src/dbus.c | 1483 -----------------
src/dbus.h | 53
src/device.c | 616 -------
src/device.h | 59
src/execute.c | 2113 ------------------------
src/execute.h | 233 --
src/fdset.c | 167 -
src/fdset.h | 40
src/ima-setup.c | 115 -
src/ima-setup.h | 29
src/initreq.h | 77
src/job.c | 701 --------
src/job.h | 200 --
src/kmod-setup.c | 96 -
src/kmod-setup.h | 27
src/load-dropin.c | 150 -
src/load-dropin.h | 31
src/load-fragment.c | 2445 ----------------------------
src/load-fragment.h | 91 -
src/locale-setup.c | 251 --
src/locale-setup.h | 27
src/manager.c | 3235 -------------------------------------
src/manager.h | 304 ---
src/mount.c | 1930 ----------------------
src/mount.h | 124 -
src/namespace.c | 346 ----
src/namespace.h | 34
src/path.c | 771 --------
src/path.h | 113 -
src/polkit.h | 36
src/securebits.h | 45
src/selinux-setup.c | 112 -
src/selinux-setup.h | 29
src/service.c | 3797 --------------------------------------------
src/service.h | 220 --
src/snapshot.c | 309 ---
src/snapshot.h | 53
src/socket.c | 2218 -------------------------
src/socket.h | 173 --
src/spawn-agent.h | 28
src/special.h | 88 -
src/swap.c | 1415 ----------------
src/swap.h | 128 -
src/sysfs-show.h | 27
src/target.c | 224 --
src/target.h | 47
src/tcpwrap.c | 68
src/tcpwrap.h | 29
src/timer.c | 520 ------
src/timer.h | 93 -
src/unit.c | 2676 -------------------------------
src/unit.h | 562 ------
186 files changed, 36208 insertions(+), 36205 deletions(-)
New commits:
commit b30e2f4c18ad81b04e4314fd191a5d458553773c
Author: Kay Sievers <kay at vrfy.org>
Date: Wed Apr 11 12:59:52 2012 +0200
move libsystemd_core.la sources into core/
diff --git a/Makefile.am b/Makefile.am
index 07b30eb..4ed1f12 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -126,6 +126,7 @@ AM_CPPFLAGS = \
-I $(top_srcdir)/src/login \
-I $(top_srcdir)/src/journal \
-I $(top_srcdir)/src/systemd \
+ -I $(top_srcdir)/src/core \
-I $(top_srcdir)/src/udev
AM_CFLAGS = $(WARNINGFLAGS)
@@ -647,128 +648,124 @@ noinst_LTLIBRARIES += \
libsystemd-core.la
libsystemd_core_la_SOURCES = \
- src/unit.c \
- src/unit.h \
- src/job.c \
- src/job.h \
- src/manager.c \
- src/manager.h \
- src/path-lookup.c \
- src/path-lookup.h \
- src/load-fragment.c \
- src/load-fragment.h \
- src/service.c \
- src/service.h \
- src/automount.c \
- src/automount.h \
- src/mount.c \
- src/mount.h \
- src/swap.c \
- src/swap.h \
- src/device.c \
- src/device.h \
- src/target.c \
- src/target.h \
- src/snapshot.c \
- src/snapshot.h \
- src/socket.c \
- src/socket.h \
- src/timer.c \
- src/timer.h \
- src/path.c \
- src/path.h \
- src/load-dropin.c \
- src/load-dropin.h \
- src/execute.c \
- src/execute.h \
- src/utmp-wtmp.c \
- src/utmp-wtmp.h \
- src/dbus.c \
- src/dbus.h \
- src/dbus-manager.c \
- src/dbus-manager.h \
- src/dbus-unit.c \
- src/dbus-unit.h \
- src/dbus-job.c \
- src/dbus-job.h \
- src/dbus-service.c \
- src/dbus-service.h \
- src/dbus-socket.c \
- src/dbus-socket.h \
- src/dbus-timer.c \
- src/dbus-timer.h \
- src/dbus-target.c \
- src/dbus-target.h \
- src/dbus-mount.c \
- src/dbus-mount.h \
- src/dbus-automount.c \
- src/dbus-automount.h \
- src/dbus-swap.c \
- src/dbus-swap.h \
- src/dbus-snapshot.c \
- src/dbus-snapshot.h \
- src/dbus-device.c \
- src/dbus-device.h \
- src/dbus-execute.c \
- src/dbus-execute.h \
- src/dbus-path.c \
- src/dbus-path.h \
- src/cgroup.c \
- src/cgroup.h \
- src/mount-setup.c \
- src/mount-setup.h \
- src/hostname-setup.c \
- src/hostname-setup.h \
- src/selinux-setup.c \
- src/selinux-setup.h \
- src/ima-setup.c \
- src/ima-setup.h \
- src/loopback-setup.h \
- src/loopback-setup.c \
- src/kmod-setup.c \
- src/kmod-setup.h \
- src/locale-setup.h \
- src/locale-setup.c \
- src/machine-id-setup.c \
- src/machine-id-setup.h \
- src/fdset.c \
- src/fdset.h \
- src/condition.c \
- src/condition.h \
+ src/def.h \
+ src/missing.h \
src/dbus-common.c \
src/dbus-common.h \
- src/install.c \
- src/install.h \
+ src/watchdog.c \
+ src/watchdog.h \
+ src/loopback-setup.h \
+ src/loopback-setup.c \
+ src/hostname-setup.c \
+ src/hostname-setup.h \
src/specifier.c \
src/specifier.h \
- src/namespace.c \
- src/namespace.h \
+ src/install.c \
+ src/install.h \
+ src/path-lookup.c \
+ src/path-lookup.h \
src/unit-name.c \
src/unit-name.h \
- src/tcpwrap.c \
- src/tcpwrap.h \
- src/cgroup-attr.c \
- src/cgroup-attr.h \
- src/watchdog.c \
- src/watchdog.h \
- src/def.h \
- src/missing.h \
- src/securebits.h \
+ src/utmp-wtmp.c \
+ src/utmp-wtmp.h \
+ src/machine-id-setup.c \
+ src/machine-id-setup.h \
+ src/mount-setup.c \
+ src/mount-setup.h \
src/linux/auto_dev-ioctl.h \
src/linux/fanotify.h \
- src/initreq.h \
- src/special.h \
- src/dbus-common.h \
- src/bus-errors.h \
- src/cgroup-show.h \
- src/build.h \
- src/umount.h \
- src/ask-password-api.h \
- src/sysfs-show.h \
- src/polkit.h \
- src/dbus-loop.h \
- src/spawn-agent.h \
- src/logs-show.h
+ src/core/unit.c \
+ src/core/unit.h \
+ src/core/job.c \
+ src/core/job.h \
+ src/core/manager.c \
+ src/core/manager.h \
+ src/core/load-fragment.c \
+ src/core/load-fragment.h \
+ src/core/service.c \
+ src/core/service.h \
+ src/core/automount.c \
+ src/core/automount.h \
+ src/core/mount.c \
+ src/core/mount.h \
+ src/core/swap.c \
+ src/core/swap.h \
+ src/core/device.c \
+ src/core/device.h \
+ src/core/target.c \
+ src/core/target.h \
+ src/core/snapshot.c \
+ src/core/snapshot.h \
+ src/core/socket.c \
+ src/core/socket.h \
+ src/core/timer.c \
+ src/core/timer.h \
+ src/core/path.c \
+ src/core/path.h \
+ src/core/load-dropin.c \
+ src/core/load-dropin.h \
+ src/core/execute.c \
+ src/core/execute.h \
+ src/core/dbus.c \
+ src/core/dbus.h \
+ src/core/dbus-manager.c \
+ src/core/dbus-manager.h \
+ src/core/dbus-unit.c \
+ src/core/dbus-unit.h \
+ src/core/dbus-job.c \
+ src/core/dbus-job.h \
+ src/core/dbus-service.c \
+ src/core/dbus-service.h \
+ src/core/dbus-socket.c \
+ src/core/dbus-socket.h \
+ src/core/dbus-timer.c \
+ src/core/dbus-timer.h \
+ src/core/dbus-target.c \
+ src/core/dbus-target.h \
+ src/core/dbus-mount.c \
+ src/core/dbus-mount.h \
+ src/core/dbus-automount.c \
+ src/core/dbus-automount.h \
+ src/core/dbus-swap.c \
+ src/core/dbus-swap.h \
+ src/core/dbus-snapshot.c \
+ src/core/dbus-snapshot.h \
+ src/core/dbus-device.c \
+ src/core/dbus-device.h \
+ src/core/dbus-execute.c \
+ src/core/dbus-execute.h \
+ src/core/dbus-path.c \
+ src/core/dbus-path.h \
+ src/core/cgroup.c \
+ src/core/cgroup.h \
+ src/core/selinux-setup.c \
+ src/core/selinux-setup.h \
+ src/core/ima-setup.c \
+ src/core/ima-setup.h \
+ src/core/kmod-setup.c \
+ src/core/kmod-setup.h \
+ src/core/locale-setup.h \
+ src/core/locale-setup.c \
+ src/core/fdset.c \
+ src/core/fdset.h \
+ src/core/condition.c \
+ src/core/condition.h \
+ src/core/namespace.c \
+ src/core/namespace.h \
+ src/core/tcpwrap.c \
+ src/core/tcpwrap.h \
+ src/core/cgroup-attr.c \
+ src/core/cgroup-attr.h \
+ src/core/securebits.h \
+ src/core/initreq.h \
+ src/core/special.h \
+ src/core/bus-errors.h \
+ src/core/build.h \
+ src/core/ask-password-api.h \
+ src/core/sysfs-show.h \
+ src/core/polkit.h \
+ src/core/dbus-loop.h \
+ src/core/spawn-agent.h
nodist_libsystemd_core_la_SOURCES = \
src/load-fragment-gperf.c \
@@ -948,6 +945,7 @@ pkginclude_HEADERS += \
systemd_shutdown_SOURCES = \
src/mount-setup.c \
src/umount.c \
+ src/umount.h \
src/shutdown.c \
src/watchdog.c \
src/watchdog.h
@@ -1075,10 +1073,12 @@ systemctl_SOURCES = \
src/dbus-common.c \
src/path-lookup.c \
src/cgroup-show.c \
+ src/cgroup-show.h \
src/unit-name.c \
src/install.c \
src/spawn-agent.c \
- src/logs-show.c
+ src/logs-show.c \
+ src/logs-show.h
systemctl_CFLAGS = \
$(AM_CFLAGS) \
@@ -1120,7 +1120,8 @@ systemd_reply_password_LDADD = \
# ------------------------------------------------------------------------------
systemd_cgls_SOURCES = \
src/cgls.c \
- src/cgroup-show.c
+ src/cgroup-show.c \
+ src/cgroup-show.h
systemd_cgls_LDADD = \
libsystemd-shared.la
@@ -1971,7 +1972,8 @@ systemd_cat_LDADD = \
journalctl_SOURCES = \
src/journal/journalctl.c \
- src/logs-show.c
+ src/logs-show.c \
+ src/logs-show.h
journalctl_LDADD = \
libsystemd-shared.la \
@@ -2585,7 +2587,8 @@ loginctl_SOURCES = \
src/login/loginctl.c \
src/login/sysfs-show.c \
src/dbus-common.c \
- src/cgroup-show.c
+ src/cgroup-show.c \
+ src/cgroup-show.h
loginctl_CFLAGS = \
$(AM_CFLAGS) \
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 5a5d027..2acacbb 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,16 +1,16 @@
-src/dbus-automount.c
-src/dbus-device.c
-src/dbus-job.c
-src/dbus-manager.c
-src/dbus-mount.c
-src/dbus-path.c
-src/dbus-service.c
-src/dbus-snapshot.c
-src/dbus-socket.c
-src/dbus-swap.c
-src/dbus-target.c
-src/dbus-timer.c
-src/dbus-unit.c
+src/core/dbus-automount.c
+src/core/dbus-device.c
+src/core/dbus-job.c
+src/core/dbus-manager.c
+src/core/dbus-mount.c
+src/core/dbus-path.c
+src/core/dbus-service.c
+src/core/dbus-snapshot.c
+src/core/dbus-socket.c
+src/core/dbus-swap.c
+src/core/dbus-target.c
+src/core/dbus-timer.c
+src/core/dbus-unit.c
src/hostname/hostnamed.c
src/locale/localed.c
src/org.freedesktop.systemd1.policy.in.in
diff --git a/src/ask-password-api.h b/src/ask-password-api.h
deleted file mode 100644
index fec8625..0000000
--- a/src/ask-password-api.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooaskpasswordapihfoo
-#define fooaskpasswordapihfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-
-int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase);
-
-int ask_password_agent(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
-
-int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
-
-#endif
diff --git a/src/automount.c b/src/automount.c
deleted file mode 100644
index 6857a6f..0000000
--- a/src/automount.c
+++ /dev/null
@@ -1,888 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <limits.h>
-#include <sys/mount.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <sys/stat.h>
-#include <linux/auto_fs4.h>
-#include <linux/auto_dev-ioctl.h>
-
-#include "unit.h"
-#include "automount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "unit-name.h"
-#include "dbus-automount.h"
-#include "bus-errors.h"
-#include "special.h"
-#include "label.h"
-#include "mkdir.h"
-
-static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
- [AUTOMOUNT_WAITING] = UNIT_ACTIVE,
- [AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
- [AUTOMOUNT_FAILED] = UNIT_FAILED
-};
-
-static int open_dev_autofs(Manager *m);
-
-static void automount_init(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- a->pipe_watch.fd = a->pipe_fd = -1;
- a->pipe_watch.type = WATCH_INVALID;
-
- a->directory_mode = 0755;
-
- UNIT(a)->ignore_on_isolate = true;
-}
-
-static void repeat_unmout(const char *path) {
- assert(path);
-
- for (;;) {
- /* If there are multiple mounts on a mount point, this
- * removes them all */
-
- if (umount2(path, MNT_DETACH) >= 0)
- continue;
-
- if (errno != EINVAL)
- log_error("Failed to unmount: %m");
-
- break;
- }
-}
-
-static void unmount_autofs(Automount *a) {
- assert(a);
-
- if (a->pipe_fd < 0)
- return;
-
- automount_send_ready(a, -EHOSTDOWN);
-
- unit_unwatch_fd(UNIT(a), &a->pipe_watch);
- close_nointr_nofail(a->pipe_fd);
- a->pipe_fd = -1;
-
- /* If we reload/reexecute things we keep the mount point
- * around */
- if (a->where &&
- (UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
- UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
- repeat_unmout(a->where);
-}
-
-static void automount_done(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- unmount_autofs(a);
- unit_ref_unset(&a->mount);
-
- free(a->where);
- a->where = NULL;
-
- set_free(a->tokens);
- a->tokens = NULL;
-}
-
-int automount_add_one_mount_link(Automount *a, Mount *m) {
- int r;
-
- assert(a);
- assert(m);
-
- if (UNIT(a)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (!path_startswith(a->where, m->where))
- return 0;
-
- if (path_equal(a->where, m->where))
- return 0;
-
- if ((r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
-
- return 0;
-}
-
-static int automount_add_mount_links(Automount *a) {
- Unit *other;
- int r;
-
- assert(a);
-
- LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT])
- if ((r = automount_add_one_mount_link(a, MOUNT(other))) < 0)
- return r;
-
- return 0;
-}
-
-static int automount_add_default_dependencies(Automount *a) {
- int r;
-
- assert(a);
-
- if (UNIT(a)->manager->running_as == MANAGER_SYSTEM) {
-
- if ((r = unit_add_dependency_by_name(UNIT(a), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int automount_verify(Automount *a) {
- bool b;
- char *e;
- assert(a);
-
- if (UNIT(a)->load_state != UNIT_LOADED)
- return 0;
-
- if (path_equal(a->where, "/")) {
- log_error("Cannot have an automount unit for the root directory. Refusing.");
- return -EINVAL;
- }
-
- if (!(e = unit_name_from_path(a->where, ".automount")))
- return -ENOMEM;
-
- b = unit_has_name(UNIT(a), e);
- free(e);
-
- if (!b) {
- log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int automount_load(Unit *u) {
- int r;
- Automount *a = AUTOMOUNT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- /* Load a .automount file */
- if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_LOADED) {
- Unit *x;
-
- if (!a->where)
- if (!(a->where = unit_name_to_path(u->id)))
- return -ENOMEM;
-
- path_kill_slashes(a->where);
-
- if ((r = automount_add_mount_links(a)) < 0)
- return r;
-
- r = unit_load_related_unit(u, ".mount", &x);
- if (r < 0)
- return r;
-
- unit_ref_set(&a->mount, x);
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true);
- if (r < 0)
- return r;
-
- if (UNIT(a)->default_dependencies)
- if ((r = automount_add_default_dependencies(a)) < 0)
- return r;
- }
-
- return automount_verify(a);
-}
-
-static void automount_set_state(Automount *a, AutomountState state) {
- AutomountState old_state;
- assert(a);
-
- old_state = a->state;
- a->state = state;
-
- if (state != AUTOMOUNT_WAITING &&
- state != AUTOMOUNT_RUNNING)
- unmount_autofs(a);
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(a)->id,
- automount_state_to_string(old_state),
- automount_state_to_string(state));
-
- unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int automount_coldplug(Unit *u) {
- Automount *a = AUTOMOUNT(u);
- int r;
-
- assert(a);
- assert(a->state == AUTOMOUNT_DEAD);
-
- if (a->deserialized_state != a->state) {
-
- if ((r = open_dev_autofs(u->manager)) < 0)
- return r;
-
- if (a->deserialized_state == AUTOMOUNT_WAITING ||
- a->deserialized_state == AUTOMOUNT_RUNNING) {
-
- assert(a->pipe_fd >= 0);
-
- if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
- return r;
- }
-
- automount_set_state(a, a->deserialized_state);
- }
-
- return 0;
-}
-
-static void automount_dump(Unit *u, FILE *f, const char *prefix) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- fprintf(f,
- "%sAutomount State: %s\n"
- "%sResult: %s\n"
- "%sWhere: %s\n"
- "%sDirectoryMode: %04o\n",
- prefix, automount_state_to_string(a->state),
- prefix, automount_result_to_string(a->result),
- prefix, a->where,
- prefix, a->directory_mode);
-}
-
-static void automount_enter_dead(Automount *a, AutomountResult f) {
- assert(a);
-
- if (f != AUTOMOUNT_SUCCESS)
- a->result = f;
-
- automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
-}
-
-static int open_dev_autofs(Manager *m) {
- struct autofs_dev_ioctl param;
-
- assert(m);
-
- if (m->dev_autofs_fd >= 0)
- return m->dev_autofs_fd;
-
- label_fix("/dev/autofs", false);
-
- if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
- log_error("Failed to open /dev/autofs: %s", strerror(errno));
- return -errno;
- }
-
- init_autofs_dev_ioctl(¶m);
- if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) {
- close_nointr_nofail(m->dev_autofs_fd);
- m->dev_autofs_fd = -1;
- return -errno;
- }
-
- log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor);
-
- return m->dev_autofs_fd;
-}
-
-static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
- struct autofs_dev_ioctl *param;
- size_t l;
- int r;
-
- assert(dev_autofs_fd >= 0);
- assert(where);
-
- l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
-
- if (!(param = malloc(l)))
- return -ENOMEM;
-
- init_autofs_dev_ioctl(param);
- param->size = l;
- param->ioctlfd = -1;
- param->openmount.devid = devid;
- strcpy(param->path, where);
-
- if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (param->ioctlfd < 0) {
- r = -EIO;
- goto finish;
- }
-
- fd_cloexec(param->ioctlfd, true);
- r = param->ioctlfd;
-
-finish:
- free(param);
- return r;
-}
-
-static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
- uint32_t major, minor;
- struct autofs_dev_ioctl param;
-
- assert(dev_autofs_fd >= 0);
- assert(ioctl_fd >= 0);
-
- init_autofs_dev_ioctl(¶m);
- param.ioctlfd = ioctl_fd;
-
- if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0)
- return -errno;
-
- major = param.protover.version;
-
- init_autofs_dev_ioctl(¶m);
- param.ioctlfd = ioctl_fd;
-
- if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0)
- return -errno;
-
- minor = param.protosubver.sub_version;
-
- log_debug("Autofs protocol version %i.%i", major, minor);
- return 0;
-}
-
-static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
- struct autofs_dev_ioctl param;
-
- assert(dev_autofs_fd >= 0);
- assert(ioctl_fd >= 0);
-
- init_autofs_dev_ioctl(¶m);
- param.ioctlfd = ioctl_fd;
- param.timeout.timeout = sec;
-
- if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0)
- return -errno;
-
- return 0;
-}
-
-static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) {
- struct autofs_dev_ioctl param;
-
- assert(dev_autofs_fd >= 0);
- assert(ioctl_fd >= 0);
-
- init_autofs_dev_ioctl(¶m);
- param.ioctlfd = ioctl_fd;
-
- if (status) {
- param.fail.token = token;
- param.fail.status = status;
- } else
- param.ready.token = token;
-
- if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0)
- return -errno;
-
- return 0;
-}
-
-int automount_send_ready(Automount *a, int status) {
- int ioctl_fd, r;
- unsigned token;
-
- assert(a);
- assert(status <= 0);
-
- if (set_isempty(a->tokens))
- return 0;
-
- if ((ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id)) < 0) {
- r = ioctl_fd;
- goto fail;
- }
-
- if (status)
- log_debug("Sending failure: %s", strerror(-status));
- else
- log_debug("Sending success.");
-
- r = 0;
-
- /* Autofs thankfully does not hand out 0 as a token */
- while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) {
- int k;
-
- /* Autofs fun fact II:
- *
- * if you pass a positive status code here, the kernel will
- * freeze! Yay! */
-
- if ((k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd,
- ioctl_fd,
- token,
- status)) < 0)
- r = k;
- }
-
-fail:
- if (ioctl_fd >= 0)
- close_nointr_nofail(ioctl_fd);
-
- return r;
-}
-
-static void automount_enter_waiting(Automount *a) {
- int p[2] = { -1, -1 };
- char name[32], options[128];
- bool mounted = false;
- int r, ioctl_fd = -1, dev_autofs_fd;
- struct stat st;
-
- assert(a);
- assert(a->pipe_fd < 0);
- assert(a->where);
-
- if (a->tokens)
- set_clear(a->tokens);
-
- if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) {
- r = dev_autofs_fd;
- goto fail;
- }
-
- /* We knowingly ignore the results of this call */
- mkdir_p(a->where, 0555);
-
- if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
- r = -errno;
- goto fail;
- }
-
- snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp());
- char_array_0(options);
-
- snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid());
- char_array_0(name);
-
- if (mount(name, a->where, "autofs", 0, options) < 0) {
- r = -errno;
- goto fail;
- }
-
- mounted = true;
-
- close_nointr_nofail(p[1]);
- p[1] = -1;
-
- if (stat(a->where, &st) < 0) {
- r = -errno;
- goto fail;
- }
-
- if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) {
- r = ioctl_fd;
- goto fail;
- }
-
- if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0)
- goto fail;
-
- if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0)
- goto fail;
-
- /* Autofs fun fact:
- *
- * Unless we close the ioctl fd here, for some weird reason
- * the direct mount will not receive events from the
- * kernel. */
-
- close_nointr_nofail(ioctl_fd);
- ioctl_fd = -1;
-
- if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0)
- goto fail;
-
- a->pipe_fd = p[0];
- a->dev_id = st.st_dev;
-
- automount_set_state(a, AUTOMOUNT_WAITING);
-
- return;
-
-fail:
- assert_se(close_pipe(p) == 0);
-
- if (ioctl_fd >= 0)
- close_nointr_nofail(ioctl_fd);
-
- if (mounted)
- repeat_unmout(a->where);
-
- log_error("Failed to initialize automounter: %s", strerror(-r));
- automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
-}
-
-static void automount_enter_runnning(Automount *a) {
- int r;
- struct stat st;
- DBusError error;
-
- assert(a);
- assert(UNIT_DEREF(a->mount));
-
- dbus_error_init(&error);
-
- /* We don't take mount requests anymore if we are supposed to
- * shut down anyway */
- if (unit_pending_inactive(UNIT(a))) {
- log_debug("Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
- automount_send_ready(a, -EHOSTDOWN);
- return;
- }
-
- mkdir_p(a->where, a->directory_mode);
-
- /* Before we do anything, let's see if somebody is playing games with us? */
- if (lstat(a->where, &st) < 0) {
- log_warning("%s failed to stat automount point: %m", UNIT(a)->id);
- goto fail;
- }
-
- if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
- log_info("%s's automount point already active?", UNIT(a)->id);
- else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
- log_warning("%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error(&error, r));
- goto fail;
- }
-
- automount_set_state(a, AUTOMOUNT_RUNNING);
- return;
-
-fail:
- automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
- dbus_error_free(&error);
-}
-
-static int automount_start(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
-
- if (path_is_mount_point(a->where, false)) {
- log_error("Path %s is already a mount point, refusing start for %s", a->where, u->id);
- return -EEXIST;
- }
-
- if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED)
- return -ENOENT;
-
- a->result = AUTOMOUNT_SUCCESS;
- automount_enter_waiting(a);
- return 0;
-}
-
-static int automount_stop(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING);
-
- automount_enter_dead(a, AUTOMOUNT_SUCCESS);
- return 0;
-}
-
-static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
- Automount *a = AUTOMOUNT(u);
- void *p;
- Iterator i;
-
- assert(a);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
- unit_serialize_item(u, f, "result", automount_result_to_string(a->result));
- unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
-
- SET_FOREACH(p, a->tokens, i)
- unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
-
- if (a->pipe_fd >= 0) {
- int copy;
-
- if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
- }
-
- return 0;
-}
-
-static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Automount *a = AUTOMOUNT(u);
- int r;
-
- assert(a);
- assert(fds);
-
- if (streq(key, "state")) {
- AutomountState state;
-
- if ((state = automount_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- a->deserialized_state = state;
- } else if (streq(key, "result")) {
- AutomountResult f;
-
- f = automount_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != AUTOMOUNT_SUCCESS)
- a->result = f;
-
- } else if (streq(key, "dev-id")) {
- unsigned d;
-
- if (safe_atou(value, &d) < 0)
- log_debug("Failed to parse dev-id value %s", value);
- else
- a->dev_id = (unsigned) d;
- } else if (streq(key, "token")) {
- unsigned token;
-
- if (safe_atou(value, &token) < 0)
- log_debug("Failed to parse token value %s", value);
- else {
- if (!a->tokens)
- if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
- return r;
- }
- } else if (streq(key, "pipe-fd")) {
- int fd;
-
- if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse pipe-fd value %s", value);
- else {
- if (a->pipe_fd >= 0)
- close_nointr_nofail(a->pipe_fd);
-
- a->pipe_fd = fdset_remove(fds, fd);
- }
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState automount_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[AUTOMOUNT(u)->state];
-}
-
-static const char *automount_sub_state_to_string(Unit *u) {
- assert(u);
-
- return automount_state_to_string(AUTOMOUNT(u)->state);
-}
-
-static bool automount_check_gc(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- if (!UNIT_DEREF(a->mount))
- return false;
-
- return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount));
-}
-
-static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
- Automount *a = AUTOMOUNT(u);
- union autofs_v5_packet_union packet;
- ssize_t l;
- int r;
-
- assert(a);
- assert(fd == a->pipe_fd);
-
- if (events != EPOLLIN) {
- log_error("Got invalid poll event on pipe.");
- goto fail;
- }
-
- if ((l = loop_read(a->pipe_fd, &packet, sizeof(packet), true)) != sizeof(packet)) {
- log_error("Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read");
- goto fail;
- }
-
- switch (packet.hdr.type) {
-
- case autofs_ptype_missing_direct:
-
- if (packet.v5_packet.pid > 0) {
- char *p = NULL;
-
- get_process_comm(packet.v5_packet.pid, &p);
- log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p));
- free(p);
-
- } else
- log_debug("Got direct mount request for %s", packet.v5_packet.name);
-
- if (!a->tokens)
- if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
- log_error("Failed to allocate token set.");
- goto fail;
- }
-
- if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
- log_error("Failed to remember token: %s", strerror(-r));
- goto fail;
- }
-
- automount_enter_runnning(a);
- break;
-
- default:
- log_error("Received unknown automount request %i", packet.hdr.type);
- break;
- }
-
- return;
-
-fail:
- automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
-}
-
-static void automount_shutdown(Manager *m) {
- assert(m);
-
- if (m->dev_autofs_fd >= 0)
- close_nointr_nofail(m->dev_autofs_fd);
-}
-
-static void automount_reset_failed(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
-
- if (a->state == AUTOMOUNT_FAILED)
- automount_set_state(a, AUTOMOUNT_DEAD);
-
- a->result = AUTOMOUNT_SUCCESS;
-}
-
-static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = "dead",
- [AUTOMOUNT_WAITING] = "waiting",
- [AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
-
-static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
- [AUTOMOUNT_SUCCESS] = "success",
- [AUTOMOUNT_FAILURE_RESOURCES] = "resources"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);
-
-const UnitVTable automount_vtable = {
- .suffix = ".automount",
- .object_size = sizeof(Automount),
- .sections =
- "Unit\0"
- "Automount\0"
- "Install\0",
-
- .no_alias = true,
- .no_instances = true,
-
- .init = automount_init,
- .load = automount_load,
- .done = automount_done,
-
- .coldplug = automount_coldplug,
-
- .dump = automount_dump,
-
- .start = automount_start,
- .stop = automount_stop,
-
- .serialize = automount_serialize,
- .deserialize_item = automount_deserialize_item,
-
- .active_state = automount_active_state,
- .sub_state_to_string = automount_sub_state_to_string,
-
- .check_gc = automount_check_gc,
-
- .fd_event = automount_fd_event,
-
- .reset_failed = automount_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Automount",
- .bus_message_handler = bus_automount_message_handler,
- .bus_invalidating_properties = bus_automount_invalidating_properties,
-
- .shutdown = automount_shutdown
-};
diff --git a/src/automount.h b/src/automount.h
deleted file mode 100644
index 19baee2..0000000
--- a/src/automount.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooautomounthfoo
-#define fooautomounthfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Automount Automount;
-
-#include "unit.h"
-
-typedef enum AutomountState {
- AUTOMOUNT_DEAD,
- AUTOMOUNT_WAITING,
- AUTOMOUNT_RUNNING,
- AUTOMOUNT_FAILED,
- _AUTOMOUNT_STATE_MAX,
- _AUTOMOUNT_STATE_INVALID = -1
-} AutomountState;
-
-typedef enum AutomountResult {
- AUTOMOUNT_SUCCESS,
- AUTOMOUNT_FAILURE_RESOURCES,
- _AUTOMOUNT_RESULT_MAX,
- _AUTOMOUNT_RESULT_INVALID = -1
-} AutomountResult;
-
-struct Automount {
- Unit meta;
-
- AutomountState state, deserialized_state;
-
- char *where;
-
- UnitRef mount;
-
- int pipe_fd;
- mode_t directory_mode;
- Watch pipe_watch;
- dev_t dev_id;
-
- Set *tokens;
-
- AutomountResult result;
-};
-
-extern const UnitVTable automount_vtable;
-
-int automount_send_ready(Automount *a, int status);
-
-int automount_add_one_mount_link(Automount *a, Mount *m);
-
-const char* automount_state_to_string(AutomountState i);
-AutomountState automount_state_from_string(const char *s);
-
-const char* automount_result_to_string(AutomountResult i);
-AutomountResult automount_result_from_string(const char *s);
-
-#endif
diff --git a/src/build.h b/src/build.h
deleted file mode 100644
index 0619013..0000000
--- a/src/build.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foobuildhfoo
-#define foobuildhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_PAM
-#define _PAM_FEATURE_ "+PAM"
-#else
-#define _PAM_FEATURE_ "-PAM"
-#endif
-
-#ifdef HAVE_LIBWRAP
-#define _LIBWRAP_FEATURE_ "+LIBWRAP"
-#else
-#define _LIBWRAP_FEATURE_ "-LIBWRAP"
-#endif
-
-#ifdef HAVE_AUDIT
-#define _AUDIT_FEATURE_ "+AUDIT"
-#else
-#define _AUDIT_FEATURE_ "-AUDIT"
-#endif
-
-#ifdef HAVE_SELINUX
-#define _SELINUX_FEATURE_ "+SELINUX"
-#else
-#define _SELINUX_FEATURE_ "-SELINUX"
-#endif
-
-#ifdef HAVE_IMA
-#define _IMA_FEATURE_ "+IMA"
-#else
-#define _IMA_FEATURE_ "-IMA"
-#endif
-
-#ifdef HAVE_SYSV_COMPAT
-#define _SYSVINIT_FEATURE_ "+SYSVINIT"
-#else
-#define _SYSVINIT_FEATURE_ "-SYSVINIT"
-#endif
-
-#ifdef HAVE_LIBCRYPTSETUP
-#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP"
-#else
-#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
-#endif
-
-#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_
-
-#endif
diff --git a/src/bus-errors.h b/src/bus-errors.h
deleted file mode 100644
index 82d4e99..0000000
--- a/src/bus-errors.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foobuserrorshfoo
-#define foobuserrorshfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <dbus/dbus.h>
-
-#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
-#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob"
-#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed"
-#define BUS_ERROR_INVALID_PATH "org.freedesktop.systemd1.InvalidPath"
-#define BUS_ERROR_INVALID_NAME "org.freedesktop.systemd1.InvalidName"
-#define BUS_ERROR_UNIT_TYPE_MISMATCH "org.freedesktop.systemd1.UnitTypeMismatch"
-#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists"
-#define BUS_ERROR_NOT_SUPPORTED "org.freedesktop.systemd1.NotSupported"
-#define BUS_ERROR_INVALID_JOB_MODE "org.freedesktop.systemd1.InvalidJobMode"
-#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency"
-#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation"
-#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed"
-#define BUS_ERROR_MASKED "org.freedesktop.systemd1.Masked"
-#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable"
-#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive"
-#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting"
-#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
-#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
-#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
-
-static inline const char *bus_error(const DBusError *e, int r) {
- if (e && e->message)
- return e->message;
-
- if (r >= 0)
- return strerror(r);
-
- return strerror(-r);
-}
-
-#endif
diff --git a/src/cgroup-attr.c b/src/cgroup-attr.c
deleted file mode 100644
index 474a686..0000000
--- a/src/cgroup-attr.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "cgroup-attr.h"
-#include "cgroup-util.h"
-#include "list.h"
-
-int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
- int r;
- char *path = NULL;
- char *v = NULL;
-
- assert(a);
-
- b = cgroup_bonding_find_list(b, a->controller);
- if (!b)
- return 0;
-
- if (a->map_callback) {
- r = a->map_callback(a->controller, a->name, a->value, &v);
- if (r < 0)
- return r;
- }
-
- r = cg_get_path(a->controller, b->path, a->name, &path);
- if (r < 0) {
- free(v);
- return r;
- }
-
- r = write_one_line_file(path, v ? v : a->value);
- if (r < 0)
- log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
-
- free(path);
- free(v);
-
- return r;
-}
-
-int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
- CGroupAttribute *a;
- int r = 0;
-
- LIST_FOREACH(by_unit, a, first) {
- int k;
-
- k = cgroup_attribute_apply(a, b);
- if (r == 0)
- r = k;
- }
-
- return r;
-}
-
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
- CGroupAttribute *a;
-
- assert(controller);
- assert(name);
-
- LIST_FOREACH(by_unit, a, first)
- if (streq(a->controller, controller) &&
- streq(a->name, name))
- return a;
-
- return NULL;
-}
-
-static void cgroup_attribute_free(CGroupAttribute *a) {
- assert(a);
-
- free(a->controller);
- free(a->name);
- free(a->value);
- free(a);
-}
-
-void cgroup_attribute_free_list(CGroupAttribute *first) {
- CGroupAttribute *a, *n;
-
- LIST_FOREACH_SAFE(by_unit, a, n, first)
- cgroup_attribute_free(a);
-}
diff --git a/src/cgroup-attr.h b/src/cgroup-attr.h
deleted file mode 100644
index 63a73b8..0000000
--- a/src/cgroup-attr.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foocgroupattrhfoo
-#define foocgroupattrhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct CGroupAttribute CGroupAttribute;
-
-typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
-
-#include "unit.h"
-#include "cgroup.h"
-
-struct CGroupAttribute {
- char *controller;
- char *name;
- char *value;
-
- CGroupAttributeMapCallback map_callback;
-
- LIST_FIELDS(CGroupAttribute, by_unit);
-};
-
-int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
-int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
-
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
-
-void cgroup_attribute_free_list(CGroupAttribute *first);
-
-#endif
diff --git a/src/cgroup.c b/src/cgroup.c
deleted file mode 100644
index 1f6139e..0000000
--- a/src/cgroup.c
+++ /dev/null
@@ -1,556 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <assert.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/mount.h>
-#include <fcntl.h>
-
-#include "cgroup.h"
-#include "cgroup-util.h"
-#include "log.h"
-
-int cgroup_bonding_realize(CGroupBonding *b) {
- int r;
-
- assert(b);
- assert(b->path);
- assert(b->controller);
-
- r = cg_create(b->controller, b->path);
- if (r < 0) {
- log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
- return r;
- }
-
- b->realized = true;
-
- return 0;
-}
-
-int cgroup_bonding_realize_list(CGroupBonding *first) {
- CGroupBonding *b;
- int r;
-
- LIST_FOREACH(by_unit, b, first)
- if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
- return r;
-
- return 0;
-}
-
-void cgroup_bonding_free(CGroupBonding *b, bool trim) {
- assert(b);
-
- if (b->unit) {
- CGroupBonding *f;
-
- LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
-
- if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
- assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
- LIST_REMOVE(CGroupBonding, by_path, f, b);
-
- if (f)
- hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
- else
- hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
- }
- }
-
- if (b->realized && b->ours && trim)
- cg_trim(b->controller, b->path, false);
-
- free(b->controller);
- free(b->path);
- free(b);
-}
-
-void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
- CGroupBonding *b, *n;
-
- LIST_FOREACH_SAFE(by_unit, b, n, first)
- cgroup_bonding_free(b, remove_or_trim);
-}
-
-void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
- assert(b);
-
- if (b->realized && b->ours)
- cg_trim(b->controller, b->path, delete_root);
-}
-
-void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
- CGroupBonding *b;
-
- LIST_FOREACH(by_unit, b, first)
- cgroup_bonding_trim(b, delete_root);
-}
-
-int cgroup_bonding_install(CGroupBonding *b, pid_t pid) {
- int r;
-
- assert(b);
- assert(pid >= 0);
-
- if ((r = cg_create_and_attach(b->controller, b->path, pid)) < 0)
- return r;
-
- b->realized = true;
- return 0;
-}
-
-int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
- CGroupBonding *b;
- int r;
-
- LIST_FOREACH(by_unit, b, first)
- if ((r = cgroup_bonding_install(b, pid)) < 0 && b->essential)
- return r;
-
- return 0;
-}
-
-int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
- assert(b);
-
- if (!b->realized)
- return -EINVAL;
-
- return cg_set_group_access(b->controller, b->path, mode, uid, gid);
-}
-
-int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
- CGroupBonding *b;
- int r;
-
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_group_access(b, mode, uid, gid);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- assert(b);
-
- if (!b->realized)
- return -EINVAL;
-
- return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
-}
-
-int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- CGroupBonding *b;
- int r;
-
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s) {
- assert(b);
- assert(sig >= 0);
-
- /* Don't kill cgroups that aren't ours */
- if (!b->ours)
- return 0;
-
- return cg_kill_recursive(b->controller, b->path, sig, sigcont, true, false, s);
-}
-
-int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s) {
- CGroupBonding *b;
- Set *allocated_set = NULL;
- int ret = -EAGAIN, r;
-
- if (!first)
- return 0;
-
- if (!s)
- if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- LIST_FOREACH(by_unit, b, first) {
- if ((r = cgroup_bonding_kill(b, sig, sigcont, s)) < 0) {
- if (r == -EAGAIN || r == -ESRCH)
- continue;
-
- ret = r;
- goto finish;
- }
-
- if (ret < 0 || r > 0)
- ret = r;
- }
-
-finish:
- if (allocated_set)
- set_free(allocated_set);
-
- return ret;
-}
-
-/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
- * cannot know */
-int cgroup_bonding_is_empty(CGroupBonding *b) {
- int r;
-
- assert(b);
-
- if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
- return r;
-
- /* If it is empty it is empty */
- if (r > 0)
- return 1;
-
- /* It's not only us using this cgroup, so we just don't know */
- return b->ours ? 0 : -EAGAIN;
-}
-
-int cgroup_bonding_is_empty_list(CGroupBonding *first) {
- CGroupBonding *b;
-
- LIST_FOREACH(by_unit, b, first) {
- int r;
-
- if ((r = cgroup_bonding_is_empty(b)) < 0) {
- /* If this returned -EAGAIN, then we don't know if the
- * group is empty, so let's see if another group can
- * tell us */
-
- if (r != -EAGAIN)
- return r;
- } else
- return r;
- }
-
- return -EAGAIN;
-}
-
-int manager_setup_cgroup(Manager *m) {
- char *current = NULL, *path = NULL;
- int r;
- char suffix[32];
-
- assert(m);
-
- /* 0. Be nice to Ingo Molnar #628004 */
- if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
- log_warning("No control group support available, not creating root group.");
- return 0;
- }
-
- /* 1. Determine hierarchy */
- if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t)) < 0) {
- log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
- goto finish;
- }
-
- if (m->running_as == MANAGER_SYSTEM)
- strcpy(suffix, "/system");
- else {
- snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
- char_array_0(suffix);
- }
-
- free(m->cgroup_hierarchy);
- if (endswith(current, suffix)) {
- /* We probably got reexecuted and can continue to use our root cgroup */
- m->cgroup_hierarchy = current;
- current = NULL;
-
- } else {
- /* We need a new root cgroup */
- m->cgroup_hierarchy = NULL;
- if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
- }
-
- /* 2. Show data */
- if ((r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path)) < 0) {
- log_error("Cannot find cgroup mount point: %s", strerror(-r));
- goto finish;
- }
-
- log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
-
- /* 3. Install agent */
- if ((r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH)) < 0)
- log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
- else if (r > 0)
- log_debug("Installed release agent.");
- else
- log_debug("Release agent already installed.");
-
- /* 4. Realize the group */
- if ((r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0)) < 0) {
- log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
- goto finish;
- }
-
- /* 5. And pin it, so that it cannot be unmounted */
- if (m->pin_cgroupfs_fd >= 0)
- close_nointr_nofail(m->pin_cgroupfs_fd);
-
- if ((m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK)) < 0) {
- log_error("Failed to open pin file: %m");
- r = -errno;
- goto finish;
- }
-
- log_debug("Created root group.");
-
-finish:
- free(current);
- free(path);
-
- return r;
-}
-
-void manager_shutdown_cgroup(Manager *m, bool delete) {
- assert(m);
-
- if (delete && m->cgroup_hierarchy)
- cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
-
- if (m->pin_cgroupfs_fd >= 0) {
- close_nointr_nofail(m->pin_cgroupfs_fd);
- m->pin_cgroupfs_fd = -1;
- }
-
- free(m->cgroup_hierarchy);
- m->cgroup_hierarchy = NULL;
-}
-
-int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
- CGroupBonding *b;
- char *p;
-
- assert(m);
- assert(cgroup);
- assert(bonding);
-
- b = hashmap_get(m->cgroup_bondings, cgroup);
- if (b) {
- *bonding = b;
- return 1;
- }
-
- p = strdup(cgroup);
- if (!p)
- return -ENOMEM;
-
- for (;;) {
- char *e;
-
- e = strrchr(p, '/');
- if (!e || e == p) {
- free(p);
- *bonding = NULL;
- return 0;
- }
-
- *e = 0;
-
- b = hashmap_get(m->cgroup_bondings, p);
- if (b) {
- free(p);
- *bonding = b;
- return 1;
- }
- }
-}
-
-int cgroup_notify_empty(Manager *m, const char *group) {
- CGroupBonding *l, *b;
- int r;
-
- assert(m);
- assert(group);
-
- r = cgroup_bonding_get(m, group, &l);
- if (r <= 0)
- return r;
-
- LIST_FOREACH(by_path, b, l) {
- int t;
-
- if (!b->unit)
- continue;
-
- t = cgroup_bonding_is_empty_list(b);
- if (t < 0) {
-
- /* If we don't know, we don't know */
- if (t != -EAGAIN)
- log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
-
- continue;
- }
-
- if (t > 0) {
- /* If it is empty, let's delete it */
- cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
-
- if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
- UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
- }
- }
-
- return 0;
-}
-
-Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
- CGroupBonding *l, *b;
- char *group = NULL;
-
- assert(m);
-
- if (pid <= 1)
- return NULL;
-
- if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
- return NULL;
-
- l = hashmap_get(m->cgroup_bondings, group);
-
- if (!l) {
- char *slash;
-
- while ((slash = strrchr(group, '/'))) {
- if (slash == group)
- break;
-
- *slash = 0;
-
- if ((l = hashmap_get(m->cgroup_bondings, group)))
- break;
- }
- }
-
- free(group);
-
- LIST_FOREACH(by_path, b, l) {
-
- if (!b->unit)
- continue;
-
- if (b->ours)
- return b->unit;
- }
-
- return NULL;
-}
-
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
- CGroupBonding *b;
-
- assert(controller);
-
- LIST_FOREACH(by_unit, b, first)
- if (streq(b->controller, controller))
- return b;
-
- return NULL;
-}
-
-char *cgroup_bonding_to_string(CGroupBonding *b) {
- char *r;
-
- assert(b);
-
- if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
- return NULL;
-
- return r;
-}
-
-pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
- FILE *f;
- pid_t pid = 0, npid, mypid;
-
- assert(b);
-
- if (!b->ours)
- return 0;
-
- if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
- return 0;
-
- mypid = getpid();
-
- while (cg_read_pid(f, &npid) > 0) {
- pid_t ppid;
-
- if (npid == pid)
- continue;
-
- /* Ignore processes that aren't our kids */
- if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
- continue;
-
- if (pid != 0) {
- /* Dang, there's more than one daemonized PID
- in this group, so we don't know what process
- is the main process. */
- pid = 0;
- break;
- }
-
- pid = npid;
- }
-
- fclose(f);
-
- return pid;
-}
-
-pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
- CGroupBonding *b;
- pid_t pid;
-
- /* Try to find a main pid from this cgroup, but checking if
- * there's only one PID in the cgroup and returning it. Later
- * on we might want to add additional, smarter heuristics
- * here. */
-
- LIST_FOREACH(by_unit, b, first)
- if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
- return pid;
-
- return 0;
-
-}
diff --git a/src/cgroup.h b/src/cgroup.h
deleted file mode 100644
index 5faa7dc..0000000
--- a/src/cgroup.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foocgrouphfoo
-#define foocgrouphfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct CGroupBonding CGroupBonding;
-
-#include "unit.h"
-
-/* Binds a cgroup to a name */
-struct CGroupBonding {
- char *controller;
- char *path;
-
- Unit *unit;
-
- /* For the Unit::cgroup_bondings list */
- LIST_FIELDS(CGroupBonding, by_unit);
-
- /* For the Manager::cgroup_bondings hashmap */
- LIST_FIELDS(CGroupBonding, by_path);
-
- /* When shutting down, remove cgroup? Are our own tasks the
- * only ones in this group?*/
- bool ours:1;
-
- /* If we cannot create this group, or add a process to it, is this fatal? */
- bool essential:1;
-
- /* This cgroup is realized */
- bool realized:1;
-};
-
-int cgroup_bonding_realize(CGroupBonding *b);
-int cgroup_bonding_realize_list(CGroupBonding *first);
-
-void cgroup_bonding_free(CGroupBonding *b, bool trim);
-void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
-
-int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
-int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
-
-int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
-int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
-
-int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s);
-int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s);
-
-void cgroup_bonding_trim(CGroupBonding *first, bool delete_root);
-void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);
-
-int cgroup_bonding_is_empty(CGroupBonding *b);
-int cgroup_bonding_is_empty_list(CGroupBonding *first);
-
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
-
-char *cgroup_bonding_to_string(CGroupBonding *b);
-
-pid_t cgroup_bonding_search_main_pid(CGroupBonding *b);
-pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b);
-
-#include "manager.h"
-
-int manager_setup_cgroup(Manager *m);
-void manager_shutdown_cgroup(Manager *m, bool delete);
-
-int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding);
-int cgroup_notify_empty(Manager *m, const char *group);
-
-Unit* cgroup_unit_by_pid(Manager *m, pid_t pid);
-
-#endif
diff --git a/src/condition.c b/src/condition.c
deleted file mode 100644
index 2b51a16..0000000
--- a/src/condition.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/capability.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
-#include "util.h"
-#include "condition.h"
-#include "virt.h"
-
-Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
- Condition *c;
-
- assert(type < _CONDITION_TYPE_MAX);
-
- c = new0(Condition, 1);
- if (!c)
- return NULL;
-
- c->type = type;
- c->trigger = trigger;
- c->negate = negate;
-
- if (parameter) {
- c->parameter = strdup(parameter);
- if (!c->parameter) {
- free(c);
- return NULL;
- }
- }
-
- return c;
-}
-
-void condition_free(Condition *c) {
- assert(c);
-
- free(c->parameter);
- free(c);
-}
-
-void condition_free_list(Condition *first) {
- Condition *c, *n;
-
- LIST_FOREACH_SAFE(conditions, c, n, first)
- condition_free(c);
-}
-
-static bool test_kernel_command_line(const char *parameter) {
- char *line, *w, *state, *word = NULL;
- bool equal;
- int r;
- size_t l, pl;
- bool found = false;
-
- assert(parameter);
-
- if (detect_container(NULL) > 0)
- return false;
-
- r = read_one_line_file("/proc/cmdline", &line);
- if (r < 0) {
- log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
- return false;
- }
-
- equal = !!strchr(parameter, '=');
- pl = strlen(parameter);
-
- FOREACH_WORD_QUOTED(w, l, line, state) {
-
- free(word);
- word = strndup(w, l);
- if (!word)
- break;
-
- if (equal) {
- if (streq(word, parameter)) {
- found = true;
- break;
- }
- } else {
- if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
- found = true;
- break;
- }
- }
-
- }
-
- free(word);
- free(line);
-
- return found;
-}
-
-static bool test_virtualization(const char *parameter) {
- int b;
- Virtualization v;
- const char *id;
-
- assert(parameter);
-
- v = detect_virtualization(&id);
- if (v < 0) {
- log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
- return false;
- }
-
- /* First, compare with yes/no */
- b = parse_boolean(parameter);
-
- if (v > 0 && b > 0)
- return true;
-
- if (v == 0 && b == 0)
- return true;
-
- /* Then, compare categorization */
- if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
- return true;
-
- if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
- return true;
-
- /* Finally compare id */
- return v > 0 && streq(parameter, id);
-}
-
-static bool test_security(const char *parameter) {
-#ifdef HAVE_SELINUX
- if (streq(parameter, "selinux"))
- return is_selinux_enabled() > 0;
-#endif
- return false;
-}
-
-static bool test_capability(const char *parameter) {
- cap_value_t value;
- FILE *f;
- char line[LINE_MAX];
- unsigned long long capabilities = (unsigned long long) -1;
-
- /* If it's an invalid capability, we don't have it */
-
- if (cap_from_name(parameter, &value) < 0)
- return false;
-
- /* If it's a valid capability we default to assume
- * that we have it */
-
- f = fopen("/proc/self/status", "re");
- if (!f)
- return true;
-
- while (fgets(line, sizeof(line), f)) {
- truncate_nl(line);
-
- if (startswith(line, "CapBnd:")) {
- (void) sscanf(line+7, "%llx", &capabilities);
- break;
- }
- }
-
- fclose(f);
-
- return !!(capabilities & (1ULL << value));
-}
-
-bool condition_test(Condition *c) {
- assert(c);
-
- switch(c->type) {
-
- case CONDITION_PATH_EXISTS:
- return (access(c->parameter, F_OK) >= 0) == !c->negate;
-
- case CONDITION_PATH_EXISTS_GLOB:
- return (glob_exists(c->parameter) > 0) == !c->negate;
-
- case CONDITION_PATH_IS_DIRECTORY: {
- struct stat st;
-
- if (stat(c->parameter, &st) < 0)
- return c->negate;
- return S_ISDIR(st.st_mode) == !c->negate;
- }
-
- case CONDITION_PATH_IS_SYMBOLIC_LINK: {
- struct stat st;
-
- if (lstat(c->parameter, &st) < 0)
- return c->negate;
- return S_ISLNK(st.st_mode) == !c->negate;
- }
-
- case CONDITION_PATH_IS_MOUNT_POINT:
- return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
-
- case CONDITION_DIRECTORY_NOT_EMPTY: {
- int k;
-
- k = dir_is_empty(c->parameter);
- return !(k == -ENOENT || k > 0) == !c->negate;
- }
-
- case CONDITION_FILE_IS_EXECUTABLE: {
- struct stat st;
-
- if (stat(c->parameter, &st) < 0)
- return c->negate;
-
- return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
- }
-
- case CONDITION_KERNEL_COMMAND_LINE:
- return test_kernel_command_line(c->parameter) == !c->negate;
-
- case CONDITION_VIRTUALIZATION:
- return test_virtualization(c->parameter) == !c->negate;
-
- case CONDITION_SECURITY:
- return test_security(c->parameter) == !c->negate;
-
- case CONDITION_CAPABILITY:
- return test_capability(c->parameter) == !c->negate;
-
- case CONDITION_NULL:
- return !c->negate;
-
- default:
- assert_not_reached("Invalid condition type.");
- }
-}
-
-bool condition_test_list(Condition *first) {
- Condition *c;
- int triggered = -1;
-
- /* If the condition list is empty, then it is true */
- if (!first)
- return true;
-
- /* Otherwise, if all of the non-trigger conditions apply and
- * if any of the trigger conditions apply (unless there are
- * none) we return true */
- LIST_FOREACH(conditions, c, first) {
- bool b;
-
- b = condition_test(c);
-
- if (!c->trigger && !b)
- return false;
-
- if (c->trigger && triggered <= 0)
- triggered = b;
- }
-
- return triggered != 0;
-}
-
-void condition_dump(Condition *c, FILE *f, const char *prefix) {
- assert(c);
- assert(f);
-
- if (!prefix)
- prefix = "";
-
- fprintf(f,
- "%s\t%s: %s%s%s\n",
- prefix,
- condition_type_to_string(c->type),
- c->trigger ? "|" : "",
- c->negate ? "!" : "",
- c->parameter);
-}
-
-void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
- Condition *c;
-
- LIST_FOREACH(conditions, c, first)
- condition_dump(c, f, prefix);
-}
-
-static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
- [CONDITION_PATH_EXISTS] = "ConditionPathExists",
- [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
- [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
- [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
- [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
- [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
- [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
- [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
- [CONDITION_SECURITY] = "ConditionSecurity",
- [CONDITION_NULL] = "ConditionNull"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
diff --git a/src/condition.h b/src/condition.h
deleted file mode 100644
index 71b1c67..0000000
--- a/src/condition.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooconditionhfoo
-#define fooconditionhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-#include "list.h"
-
-typedef enum ConditionType {
- CONDITION_PATH_EXISTS,
- CONDITION_PATH_EXISTS_GLOB,
- CONDITION_PATH_IS_DIRECTORY,
- CONDITION_PATH_IS_SYMBOLIC_LINK,
- CONDITION_PATH_IS_MOUNT_POINT,
- CONDITION_DIRECTORY_NOT_EMPTY,
- CONDITION_FILE_IS_EXECUTABLE,
- CONDITION_KERNEL_COMMAND_LINE,
- CONDITION_VIRTUALIZATION,
- CONDITION_SECURITY,
- CONDITION_CAPABILITY,
- CONDITION_NULL,
- _CONDITION_TYPE_MAX,
- _CONDITION_TYPE_INVALID = -1
-} ConditionType;
-
-typedef struct Condition {
- ConditionType type;
- char *parameter;
-
- bool trigger:1;
- bool negate:1;
-
- LIST_FIELDS(struct Condition, conditions);
-} Condition;
-
-Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate);
-void condition_free(Condition *c);
-void condition_free_list(Condition *c);
-
-bool condition_test(Condition *c);
-bool condition_test_list(Condition *c);
-
-void condition_dump(Condition *c, FILE *f, const char *prefix);
-void condition_dump_list(Condition *c, FILE *f, const char *prefix);
-
-const char* condition_type_to_string(ConditionType t);
-int condition_type_from_string(const char *s);
-
-#endif
diff --git a/src/core/ask-password-api.h b/src/core/ask-password-api.h
new file mode 100644
index 0000000..fec8625
--- /dev/null
+++ b/src/core/ask-password-api.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooaskpasswordapihfoo
+#define fooaskpasswordapihfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase);
+
+int ask_password_agent(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
+
+int ask_password_auto(const char *message, const char *icon, usec_t until, bool accept_cached, char ***_passphrases);
+
+#endif
diff --git a/src/core/automount.c b/src/core/automount.c
new file mode 100644
index 0000000..6857a6f
--- /dev/null
+++ b/src/core/automount.c
@@ -0,0 +1,888 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <linux/auto_fs4.h>
+#include <linux/auto_dev-ioctl.h>
+
+#include "unit.h"
+#include "automount.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "unit-name.h"
+#include "dbus-automount.h"
+#include "bus-errors.h"
+#include "special.h"
+#include "label.h"
+#include "mkdir.h"
+
+static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
+ [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
+ [AUTOMOUNT_WAITING] = UNIT_ACTIVE,
+ [AUTOMOUNT_RUNNING] = UNIT_ACTIVE,
+ [AUTOMOUNT_FAILED] = UNIT_FAILED
+};
+
+static int open_dev_autofs(Manager *m);
+
+static void automount_init(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ a->pipe_watch.fd = a->pipe_fd = -1;
+ a->pipe_watch.type = WATCH_INVALID;
+
+ a->directory_mode = 0755;
+
+ UNIT(a)->ignore_on_isolate = true;
+}
+
+static void repeat_unmout(const char *path) {
+ assert(path);
+
+ for (;;) {
+ /* If there are multiple mounts on a mount point, this
+ * removes them all */
+
+ if (umount2(path, MNT_DETACH) >= 0)
+ continue;
+
+ if (errno != EINVAL)
+ log_error("Failed to unmount: %m");
+
+ break;
+ }
+}
+
+static void unmount_autofs(Automount *a) {
+ assert(a);
+
+ if (a->pipe_fd < 0)
+ return;
+
+ automount_send_ready(a, -EHOSTDOWN);
+
+ unit_unwatch_fd(UNIT(a), &a->pipe_watch);
+ close_nointr_nofail(a->pipe_fd);
+ a->pipe_fd = -1;
+
+ /* If we reload/reexecute things we keep the mount point
+ * around */
+ if (a->where &&
+ (UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
+ UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
+ repeat_unmout(a->where);
+}
+
+static void automount_done(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ unmount_autofs(a);
+ unit_ref_unset(&a->mount);
+
+ free(a->where);
+ a->where = NULL;
+
+ set_free(a->tokens);
+ a->tokens = NULL;
+}
+
+int automount_add_one_mount_link(Automount *a, Mount *m) {
+ int r;
+
+ assert(a);
+ assert(m);
+
+ if (UNIT(a)->load_state != UNIT_LOADED ||
+ UNIT(m)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!path_startswith(a->where, m->where))
+ return 0;
+
+ if (path_equal(a->where, m->where))
+ return 0;
+
+ if ((r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int automount_add_mount_links(Automount *a) {
+ Unit *other;
+ int r;
+
+ assert(a);
+
+ LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT])
+ if ((r = automount_add_one_mount_link(a, MOUNT(other))) < 0)
+ return r;
+
+ return 0;
+}
+
+static int automount_add_default_dependencies(Automount *a) {
+ int r;
+
+ assert(a);
+
+ if (UNIT(a)->manager->running_as == MANAGER_SYSTEM) {
+
+ if ((r = unit_add_dependency_by_name(UNIT(a), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ return r;
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int automount_verify(Automount *a) {
+ bool b;
+ char *e;
+ assert(a);
+
+ if (UNIT(a)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (path_equal(a->where, "/")) {
+ log_error("Cannot have an automount unit for the root directory. Refusing.");
+ return -EINVAL;
+ }
+
+ if (!(e = unit_name_from_path(a->where, ".automount")))
+ return -ENOMEM;
+
+ b = unit_has_name(UNIT(a), e);
+ free(e);
+
+ if (!b) {
+ log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int automount_load(Unit *u) {
+ int r;
+ Automount *a = AUTOMOUNT(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ /* Load a .automount file */
+ if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_LOADED) {
+ Unit *x;
+
+ if (!a->where)
+ if (!(a->where = unit_name_to_path(u->id)))
+ return -ENOMEM;
+
+ path_kill_slashes(a->where);
+
+ if ((r = automount_add_mount_links(a)) < 0)
+ return r;
+
+ r = unit_load_related_unit(u, ".mount", &x);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&a->mount, x);
+
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true);
+ if (r < 0)
+ return r;
+
+ if (UNIT(a)->default_dependencies)
+ if ((r = automount_add_default_dependencies(a)) < 0)
+ return r;
+ }
+
+ return automount_verify(a);
+}
+
+static void automount_set_state(Automount *a, AutomountState state) {
+ AutomountState old_state;
+ assert(a);
+
+ old_state = a->state;
+ a->state = state;
+
+ if (state != AUTOMOUNT_WAITING &&
+ state != AUTOMOUNT_RUNNING)
+ unmount_autofs(a);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(a)->id,
+ automount_state_to_string(old_state),
+ automount_state_to_string(state));
+
+ unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int automount_coldplug(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
+ assert(a);
+ assert(a->state == AUTOMOUNT_DEAD);
+
+ if (a->deserialized_state != a->state) {
+
+ if ((r = open_dev_autofs(u->manager)) < 0)
+ return r;
+
+ if (a->deserialized_state == AUTOMOUNT_WAITING ||
+ a->deserialized_state == AUTOMOUNT_RUNNING) {
+
+ assert(a->pipe_fd >= 0);
+
+ if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
+ return r;
+ }
+
+ automount_set_state(a, a->deserialized_state);
+ }
+
+ return 0;
+}
+
+static void automount_dump(Unit *u, FILE *f, const char *prefix) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ fprintf(f,
+ "%sAutomount State: %s\n"
+ "%sResult: %s\n"
+ "%sWhere: %s\n"
+ "%sDirectoryMode: %04o\n",
+ prefix, automount_state_to_string(a->state),
+ prefix, automount_result_to_string(a->result),
+ prefix, a->where,
+ prefix, a->directory_mode);
+}
+
+static void automount_enter_dead(Automount *a, AutomountResult f) {
+ assert(a);
+
+ if (f != AUTOMOUNT_SUCCESS)
+ a->result = f;
+
+ automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
+}
+
+static int open_dev_autofs(Manager *m) {
+ struct autofs_dev_ioctl param;
+
+ assert(m);
+
+ if (m->dev_autofs_fd >= 0)
+ return m->dev_autofs_fd;
+
+ label_fix("/dev/autofs", false);
+
+ if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
+ log_error("Failed to open /dev/autofs: %s", strerror(errno));
+ return -errno;
+ }
+
+ init_autofs_dev_ioctl(¶m);
+ if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) {
+ close_nointr_nofail(m->dev_autofs_fd);
+ m->dev_autofs_fd = -1;
+ return -errno;
+ }
+
+ log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor);
+
+ return m->dev_autofs_fd;
+}
+
+static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
+ struct autofs_dev_ioctl *param;
+ size_t l;
+ int r;
+
+ assert(dev_autofs_fd >= 0);
+ assert(where);
+
+ l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
+
+ if (!(param = malloc(l)))
+ return -ENOMEM;
+
+ init_autofs_dev_ioctl(param);
+ param->size = l;
+ param->ioctlfd = -1;
+ param->openmount.devid = devid;
+ strcpy(param->path, where);
+
+ if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (param->ioctlfd < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ fd_cloexec(param->ioctlfd, true);
+ r = param->ioctlfd;
+
+finish:
+ free(param);
+ return r;
+}
+
+static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
+ uint32_t major, minor;
+ struct autofs_dev_ioctl param;
+
+ assert(dev_autofs_fd >= 0);
+ assert(ioctl_fd >= 0);
+
+ init_autofs_dev_ioctl(¶m);
+ param.ioctlfd = ioctl_fd;
+
+ if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0)
+ return -errno;
+
+ major = param.protover.version;
+
+ init_autofs_dev_ioctl(¶m);
+ param.ioctlfd = ioctl_fd;
+
+ if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0)
+ return -errno;
+
+ minor = param.protosubver.sub_version;
+
+ log_debug("Autofs protocol version %i.%i", major, minor);
+ return 0;
+}
+
+static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, time_t sec) {
+ struct autofs_dev_ioctl param;
+
+ assert(dev_autofs_fd >= 0);
+ assert(ioctl_fd >= 0);
+
+ init_autofs_dev_ioctl(¶m);
+ param.ioctlfd = ioctl_fd;
+ param.timeout.timeout = sec;
+
+ if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) {
+ struct autofs_dev_ioctl param;
+
+ assert(dev_autofs_fd >= 0);
+ assert(ioctl_fd >= 0);
+
+ init_autofs_dev_ioctl(¶m);
+ param.ioctlfd = ioctl_fd;
+
+ if (status) {
+ param.fail.token = token;
+ param.fail.status = status;
+ } else
+ param.ready.token = token;
+
+ if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int automount_send_ready(Automount *a, int status) {
+ int ioctl_fd, r;
+ unsigned token;
+
+ assert(a);
+ assert(status <= 0);
+
+ if (set_isempty(a->tokens))
+ return 0;
+
+ if ((ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id)) < 0) {
+ r = ioctl_fd;
+ goto fail;
+ }
+
+ if (status)
+ log_debug("Sending failure: %s", strerror(-status));
+ else
+ log_debug("Sending success.");
+
+ r = 0;
+
+ /* Autofs thankfully does not hand out 0 as a token */
+ while ((token = PTR_TO_UINT(set_steal_first(a->tokens)))) {
+ int k;
+
+ /* Autofs fun fact II:
+ *
+ * if you pass a positive status code here, the kernel will
+ * freeze! Yay! */
+
+ if ((k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd,
+ ioctl_fd,
+ token,
+ status)) < 0)
+ r = k;
+ }
+
+fail:
+ if (ioctl_fd >= 0)
+ close_nointr_nofail(ioctl_fd);
+
+ return r;
+}
+
+static void automount_enter_waiting(Automount *a) {
+ int p[2] = { -1, -1 };
+ char name[32], options[128];
+ bool mounted = false;
+ int r, ioctl_fd = -1, dev_autofs_fd;
+ struct stat st;
+
+ assert(a);
+ assert(a->pipe_fd < 0);
+ assert(a->where);
+
+ if (a->tokens)
+ set_clear(a->tokens);
+
+ if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) {
+ r = dev_autofs_fd;
+ goto fail;
+ }
+
+ /* We knowingly ignore the results of this call */
+ mkdir_p(a->where, 0555);
+
+ if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp());
+ char_array_0(options);
+
+ snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid());
+ char_array_0(name);
+
+ if (mount(name, a->where, "autofs", 0, options) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ mounted = true;
+
+ close_nointr_nofail(p[1]);
+ p[1] = -1;
+
+ if (stat(a->where, &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) {
+ r = ioctl_fd;
+ goto fail;
+ }
+
+ if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0)
+ goto fail;
+
+ if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0)
+ goto fail;
+
+ /* Autofs fun fact:
+ *
+ * Unless we close the ioctl fd here, for some weird reason
+ * the direct mount will not receive events from the
+ * kernel. */
+
+ close_nointr_nofail(ioctl_fd);
+ ioctl_fd = -1;
+
+ if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0)
+ goto fail;
+
+ a->pipe_fd = p[0];
+ a->dev_id = st.st_dev;
+
+ automount_set_state(a, AUTOMOUNT_WAITING);
+
+ return;
+
+fail:
+ assert_se(close_pipe(p) == 0);
+
+ if (ioctl_fd >= 0)
+ close_nointr_nofail(ioctl_fd);
+
+ if (mounted)
+ repeat_unmout(a->where);
+
+ log_error("Failed to initialize automounter: %s", strerror(-r));
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
+}
+
+static void automount_enter_runnning(Automount *a) {
+ int r;
+ struct stat st;
+ DBusError error;
+
+ assert(a);
+ assert(UNIT_DEREF(a->mount));
+
+ dbus_error_init(&error);
+
+ /* We don't take mount requests anymore if we are supposed to
+ * shut down anyway */
+ if (unit_pending_inactive(UNIT(a))) {
+ log_debug("Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
+ automount_send_ready(a, -EHOSTDOWN);
+ return;
+ }
+
+ mkdir_p(a->where, a->directory_mode);
+
+ /* Before we do anything, let's see if somebody is playing games with us? */
+ if (lstat(a->where, &st) < 0) {
+ log_warning("%s failed to stat automount point: %m", UNIT(a)->id);
+ goto fail;
+ }
+
+ if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
+ log_info("%s's automount point already active?", UNIT(a)->id);
+ else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
+ log_warning("%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error(&error, r));
+ goto fail;
+ }
+
+ automount_set_state(a, AUTOMOUNT_RUNNING);
+ return;
+
+fail:
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
+ dbus_error_free(&error);
+}
+
+static int automount_start(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
+
+ if (path_is_mount_point(a->where, false)) {
+ log_error("Path %s is already a mount point, refusing start for %s", a->where, u->id);
+ return -EEXIST;
+ }
+
+ if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED)
+ return -ENOENT;
+
+ a->result = AUTOMOUNT_SUCCESS;
+ automount_enter_waiting(a);
+ return 0;
+}
+
+static int automount_stop(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING);
+
+ automount_enter_dead(a, AUTOMOUNT_SUCCESS);
+ return 0;
+}
+
+static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Automount *a = AUTOMOUNT(u);
+ void *p;
+ Iterator i;
+
+ assert(a);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", automount_state_to_string(a->state));
+ unit_serialize_item(u, f, "result", automount_result_to_string(a->result));
+ unit_serialize_item_format(u, f, "dev-id", "%u", (unsigned) a->dev_id);
+
+ SET_FOREACH(p, a->tokens, i)
+ unit_serialize_item_format(u, f, "token", "%u", PTR_TO_UINT(p));
+
+ if (a->pipe_fd >= 0) {
+ int copy;
+
+ if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
+ }
+
+ return 0;
+}
+
+static int automount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
+ assert(a);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ AutomountState state;
+
+ if ((state = automount_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ a->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ AutomountResult f;
+
+ f = automount_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != AUTOMOUNT_SUCCESS)
+ a->result = f;
+
+ } else if (streq(key, "dev-id")) {
+ unsigned d;
+
+ if (safe_atou(value, &d) < 0)
+ log_debug("Failed to parse dev-id value %s", value);
+ else
+ a->dev_id = (unsigned) d;
+ } else if (streq(key, "token")) {
+ unsigned token;
+
+ if (safe_atou(value, &token) < 0)
+ log_debug("Failed to parse token value %s", value);
+ else {
+ if (!a->tokens)
+ if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
+ return r;
+ }
+ } else if (streq(key, "pipe-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse pipe-fd value %s", value);
+ else {
+ if (a->pipe_fd >= 0)
+ close_nointr_nofail(a->pipe_fd);
+
+ a->pipe_fd = fdset_remove(fds, fd);
+ }
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState automount_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[AUTOMOUNT(u)->state];
+}
+
+static const char *automount_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return automount_state_to_string(AUTOMOUNT(u)->state);
+}
+
+static bool automount_check_gc(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ if (!UNIT_DEREF(a->mount))
+ return false;
+
+ return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount));
+}
+
+static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
+ Automount *a = AUTOMOUNT(u);
+ union autofs_v5_packet_union packet;
+ ssize_t l;
+ int r;
+
+ assert(a);
+ assert(fd == a->pipe_fd);
+
+ if (events != EPOLLIN) {
+ log_error("Got invalid poll event on pipe.");
+ goto fail;
+ }
+
+ if ((l = loop_read(a->pipe_fd, &packet, sizeof(packet), true)) != sizeof(packet)) {
+ log_error("Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read");
+ goto fail;
+ }
+
+ switch (packet.hdr.type) {
+
+ case autofs_ptype_missing_direct:
+
+ if (packet.v5_packet.pid > 0) {
+ char *p = NULL;
+
+ get_process_comm(packet.v5_packet.pid, &p);
+ log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p));
+ free(p);
+
+ } else
+ log_debug("Got direct mount request for %s", packet.v5_packet.name);
+
+ if (!a->tokens)
+ if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
+ log_error("Failed to allocate token set.");
+ goto fail;
+ }
+
+ if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
+ log_error("Failed to remember token: %s", strerror(-r));
+ goto fail;
+ }
+
+ automount_enter_runnning(a);
+ break;
+
+ default:
+ log_error("Received unknown automount request %i", packet.hdr.type);
+ break;
+ }
+
+ return;
+
+fail:
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
+}
+
+static void automount_shutdown(Manager *m) {
+ assert(m);
+
+ if (m->dev_autofs_fd >= 0)
+ close_nointr_nofail(m->dev_autofs_fd);
+}
+
+static void automount_reset_failed(Unit *u) {
+ Automount *a = AUTOMOUNT(u);
+
+ assert(a);
+
+ if (a->state == AUTOMOUNT_FAILED)
+ automount_set_state(a, AUTOMOUNT_DEAD);
+
+ a->result = AUTOMOUNT_SUCCESS;
+}
+
+static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
+ [AUTOMOUNT_DEAD] = "dead",
+ [AUTOMOUNT_WAITING] = "waiting",
+ [AUTOMOUNT_RUNNING] = "running",
+ [AUTOMOUNT_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
+
+static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
+ [AUTOMOUNT_SUCCESS] = "success",
+ [AUTOMOUNT_FAILURE_RESOURCES] = "resources"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);
+
+const UnitVTable automount_vtable = {
+ .suffix = ".automount",
+ .object_size = sizeof(Automount),
+ .sections =
+ "Unit\0"
+ "Automount\0"
+ "Install\0",
+
+ .no_alias = true,
+ .no_instances = true,
+
+ .init = automount_init,
+ .load = automount_load,
+ .done = automount_done,
+
+ .coldplug = automount_coldplug,
+
+ .dump = automount_dump,
+
+ .start = automount_start,
+ .stop = automount_stop,
+
+ .serialize = automount_serialize,
+ .deserialize_item = automount_deserialize_item,
+
+ .active_state = automount_active_state,
+ .sub_state_to_string = automount_sub_state_to_string,
+
+ .check_gc = automount_check_gc,
+
+ .fd_event = automount_fd_event,
+
+ .reset_failed = automount_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Automount",
+ .bus_message_handler = bus_automount_message_handler,
+ .bus_invalidating_properties = bus_automount_invalidating_properties,
+
+ .shutdown = automount_shutdown
+};
diff --git a/src/core/automount.h b/src/core/automount.h
new file mode 100644
index 0000000..19baee2
--- /dev/null
+++ b/src/core/automount.h
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooautomounthfoo
+#define fooautomounthfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Automount Automount;
+
+#include "unit.h"
+
+typedef enum AutomountState {
+ AUTOMOUNT_DEAD,
+ AUTOMOUNT_WAITING,
+ AUTOMOUNT_RUNNING,
+ AUTOMOUNT_FAILED,
+ _AUTOMOUNT_STATE_MAX,
+ _AUTOMOUNT_STATE_INVALID = -1
+} AutomountState;
+
+typedef enum AutomountResult {
+ AUTOMOUNT_SUCCESS,
+ AUTOMOUNT_FAILURE_RESOURCES,
+ _AUTOMOUNT_RESULT_MAX,
+ _AUTOMOUNT_RESULT_INVALID = -1
+} AutomountResult;
+
+struct Automount {
+ Unit meta;
+
+ AutomountState state, deserialized_state;
+
+ char *where;
+
+ UnitRef mount;
+
+ int pipe_fd;
+ mode_t directory_mode;
+ Watch pipe_watch;
+ dev_t dev_id;
+
+ Set *tokens;
+
+ AutomountResult result;
+};
+
+extern const UnitVTable automount_vtable;
+
+int automount_send_ready(Automount *a, int status);
+
+int automount_add_one_mount_link(Automount *a, Mount *m);
+
+const char* automount_state_to_string(AutomountState i);
+AutomountState automount_state_from_string(const char *s);
+
+const char* automount_result_to_string(AutomountResult i);
+AutomountResult automount_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/build.h b/src/core/build.h
new file mode 100644
index 0000000..0619013
--- /dev/null
+++ b/src/core/build.h
@@ -0,0 +1,69 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foobuildhfoo
+#define foobuildhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_PAM
+#define _PAM_FEATURE_ "+PAM"
+#else
+#define _PAM_FEATURE_ "-PAM"
+#endif
+
+#ifdef HAVE_LIBWRAP
+#define _LIBWRAP_FEATURE_ "+LIBWRAP"
+#else
+#define _LIBWRAP_FEATURE_ "-LIBWRAP"
+#endif
+
+#ifdef HAVE_AUDIT
+#define _AUDIT_FEATURE_ "+AUDIT"
+#else
+#define _AUDIT_FEATURE_ "-AUDIT"
+#endif
+
+#ifdef HAVE_SELINUX
+#define _SELINUX_FEATURE_ "+SELINUX"
+#else
+#define _SELINUX_FEATURE_ "-SELINUX"
+#endif
+
+#ifdef HAVE_IMA
+#define _IMA_FEATURE_ "+IMA"
+#else
+#define _IMA_FEATURE_ "-IMA"
+#endif
+
+#ifdef HAVE_SYSV_COMPAT
+#define _SYSVINIT_FEATURE_ "+SYSVINIT"
+#else
+#define _SYSVINIT_FEATURE_ "-SYSVINIT"
+#endif
+
+#ifdef HAVE_LIBCRYPTSETUP
+#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP"
+#else
+#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
+#endif
+
+#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_
+
+#endif
diff --git a/src/core/bus-errors.h b/src/core/bus-errors.h
new file mode 100644
index 0000000..82d4e99
--- /dev/null
+++ b/src/core/bus-errors.h
@@ -0,0 +1,58 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foobuserrorshfoo
+#define foobuserrorshfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <dbus/dbus.h>
+
+#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
+#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob"
+#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed"
+#define BUS_ERROR_INVALID_PATH "org.freedesktop.systemd1.InvalidPath"
+#define BUS_ERROR_INVALID_NAME "org.freedesktop.systemd1.InvalidName"
+#define BUS_ERROR_UNIT_TYPE_MISMATCH "org.freedesktop.systemd1.UnitTypeMismatch"
+#define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists"
+#define BUS_ERROR_NOT_SUPPORTED "org.freedesktop.systemd1.NotSupported"
+#define BUS_ERROR_INVALID_JOB_MODE "org.freedesktop.systemd1.InvalidJobMode"
+#define BUS_ERROR_ONLY_BY_DEPENDENCY "org.freedesktop.systemd1.OnlyByDependency"
+#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation"
+#define BUS_ERROR_LOAD_FAILED "org.freedesktop.systemd1.LoadFailed"
+#define BUS_ERROR_MASKED "org.freedesktop.systemd1.Masked"
+#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable"
+#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive"
+#define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting"
+#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
+#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
+#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
+
+static inline const char *bus_error(const DBusError *e, int r) {
+ if (e && e->message)
+ return e->message;
+
+ if (r >= 0)
+ return strerror(r);
+
+ return strerror(-r);
+}
+
+#endif
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
new file mode 100644
index 0000000..474a686
--- /dev/null
+++ b/src/core/cgroup-attr.c
@@ -0,0 +1,102 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "cgroup-attr.h"
+#include "cgroup-util.h"
+#include "list.h"
+
+int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
+ int r;
+ char *path = NULL;
+ char *v = NULL;
+
+ assert(a);
+
+ b = cgroup_bonding_find_list(b, a->controller);
+ if (!b)
+ return 0;
+
+ if (a->map_callback) {
+ r = a->map_callback(a->controller, a->name, a->value, &v);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_get_path(a->controller, b->path, a->name, &path);
+ if (r < 0) {
+ free(v);
+ return r;
+ }
+
+ r = write_one_line_file(path, v ? v : a->value);
+ if (r < 0)
+ log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
+
+ free(path);
+ free(v);
+
+ return r;
+}
+
+int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
+ CGroupAttribute *a;
+ int r = 0;
+
+ LIST_FOREACH(by_unit, a, first) {
+ int k;
+
+ k = cgroup_attribute_apply(a, b);
+ if (r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
+ CGroupAttribute *a;
+
+ assert(controller);
+ assert(name);
+
+ LIST_FOREACH(by_unit, a, first)
+ if (streq(a->controller, controller) &&
+ streq(a->name, name))
+ return a;
+
+ return NULL;
+}
+
+static void cgroup_attribute_free(CGroupAttribute *a) {
+ assert(a);
+
+ free(a->controller);
+ free(a->name);
+ free(a->value);
+ free(a);
+}
+
+void cgroup_attribute_free_list(CGroupAttribute *first) {
+ CGroupAttribute *a, *n;
+
+ LIST_FOREACH_SAFE(by_unit, a, n, first)
+ cgroup_attribute_free(a);
+}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
new file mode 100644
index 0000000..63a73b8
--- /dev/null
+++ b/src/core/cgroup-attr.h
@@ -0,0 +1,49 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocgroupattrhfoo
+#define foocgroupattrhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct CGroupAttribute CGroupAttribute;
+
+typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
+
+#include "unit.h"
+#include "cgroup.h"
+
+struct CGroupAttribute {
+ char *controller;
+ char *name;
+ char *value;
+
+ CGroupAttributeMapCallback map_callback;
+
+ LIST_FIELDS(CGroupAttribute, by_unit);
+};
+
+int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
+int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
+
+CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
+
+void cgroup_attribute_free_list(CGroupAttribute *first);
+
+#endif
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
new file mode 100644
index 0000000..1f6139e
--- /dev/null
+++ b/src/core/cgroup.c
@@ -0,0 +1,556 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+
+#include "cgroup.h"
+#include "cgroup-util.h"
+#include "log.h"
+
+int cgroup_bonding_realize(CGroupBonding *b) {
+ int r;
+
+ assert(b);
+ assert(b->path);
+ assert(b->controller);
+
+ r = cg_create(b->controller, b->path);
+ if (r < 0) {
+ log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
+ return r;
+ }
+
+ b->realized = true;
+
+ return 0;
+}
+
+int cgroup_bonding_realize_list(CGroupBonding *first) {
+ CGroupBonding *b;
+ int r;
+
+ LIST_FOREACH(by_unit, b, first)
+ if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
+ return r;
+
+ return 0;
+}
+
+void cgroup_bonding_free(CGroupBonding *b, bool trim) {
+ assert(b);
+
+ if (b->unit) {
+ CGroupBonding *f;
+
+ LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
+
+ if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
+ LIST_REMOVE(CGroupBonding, by_path, f, b);
+
+ if (f)
+ hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
+ else
+ hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
+ }
+ }
+
+ if (b->realized && b->ours && trim)
+ cg_trim(b->controller, b->path, false);
+
+ free(b->controller);
+ free(b->path);
+ free(b);
+}
+
+void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
+ CGroupBonding *b, *n;
+
+ LIST_FOREACH_SAFE(by_unit, b, n, first)
+ cgroup_bonding_free(b, remove_or_trim);
+}
+
+void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
+ assert(b);
+
+ if (b->realized && b->ours)
+ cg_trim(b->controller, b->path, delete_root);
+}
+
+void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
+ CGroupBonding *b;
+
+ LIST_FOREACH(by_unit, b, first)
+ cgroup_bonding_trim(b, delete_root);
+}
+
+int cgroup_bonding_install(CGroupBonding *b, pid_t pid) {
+ int r;
+
+ assert(b);
+ assert(pid >= 0);
+
+ if ((r = cg_create_and_attach(b->controller, b->path, pid)) < 0)
+ return r;
+
+ b->realized = true;
+ return 0;
+}
+
+int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
+ CGroupBonding *b;
+ int r;
+
+ LIST_FOREACH(by_unit, b, first)
+ if ((r = cgroup_bonding_install(b, pid)) < 0 && b->essential)
+ return r;
+
+ return 0;
+}
+
+int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
+ assert(b);
+
+ if (!b->realized)
+ return -EINVAL;
+
+ return cg_set_group_access(b->controller, b->path, mode, uid, gid);
+}
+
+int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
+ CGroupBonding *b;
+ int r;
+
+ LIST_FOREACH(by_unit, b, first) {
+ r = cgroup_bonding_set_group_access(b, mode, uid, gid);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
+ assert(b);
+
+ if (!b->realized)
+ return -EINVAL;
+
+ return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
+}
+
+int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
+ CGroupBonding *b;
+ int r;
+
+ LIST_FOREACH(by_unit, b, first) {
+ r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s) {
+ assert(b);
+ assert(sig >= 0);
+
+ /* Don't kill cgroups that aren't ours */
+ if (!b->ours)
+ return 0;
+
+ return cg_kill_recursive(b->controller, b->path, sig, sigcont, true, false, s);
+}
+
+int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s) {
+ CGroupBonding *b;
+ Set *allocated_set = NULL;
+ int ret = -EAGAIN, r;
+
+ if (!first)
+ return 0;
+
+ if (!s)
+ if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ LIST_FOREACH(by_unit, b, first) {
+ if ((r = cgroup_bonding_kill(b, sig, sigcont, s)) < 0) {
+ if (r == -EAGAIN || r == -ESRCH)
+ continue;
+
+ ret = r;
+ goto finish;
+ }
+
+ if (ret < 0 || r > 0)
+ ret = r;
+ }
+
+finish:
+ if (allocated_set)
+ set_free(allocated_set);
+
+ return ret;
+}
+
+/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
+ * cannot know */
+int cgroup_bonding_is_empty(CGroupBonding *b) {
+ int r;
+
+ assert(b);
+
+ if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
+ return r;
+
+ /* If it is empty it is empty */
+ if (r > 0)
+ return 1;
+
+ /* It's not only us using this cgroup, so we just don't know */
+ return b->ours ? 0 : -EAGAIN;
+}
+
+int cgroup_bonding_is_empty_list(CGroupBonding *first) {
+ CGroupBonding *b;
+
+ LIST_FOREACH(by_unit, b, first) {
+ int r;
+
+ if ((r = cgroup_bonding_is_empty(b)) < 0) {
+ /* If this returned -EAGAIN, then we don't know if the
+ * group is empty, so let's see if another group can
+ * tell us */
+
+ if (r != -EAGAIN)
+ return r;
+ } else
+ return r;
+ }
+
+ return -EAGAIN;
+}
+
+int manager_setup_cgroup(Manager *m) {
+ char *current = NULL, *path = NULL;
+ int r;
+ char suffix[32];
+
+ assert(m);
+
+ /* 0. Be nice to Ingo Molnar #628004 */
+ if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
+ log_warning("No control group support available, not creating root group.");
+ return 0;
+ }
+
+ /* 1. Determine hierarchy */
+ if ((r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t)) < 0) {
+ log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
+ goto finish;
+ }
+
+ if (m->running_as == MANAGER_SYSTEM)
+ strcpy(suffix, "/system");
+ else {
+ snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
+ char_array_0(suffix);
+ }
+
+ free(m->cgroup_hierarchy);
+ if (endswith(current, suffix)) {
+ /* We probably got reexecuted and can continue to use our root cgroup */
+ m->cgroup_hierarchy = current;
+ current = NULL;
+
+ } else {
+ /* We need a new root cgroup */
+ m->cgroup_hierarchy = NULL;
+ if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+ }
+
+ /* 2. Show data */
+ if ((r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path)) < 0) {
+ log_error("Cannot find cgroup mount point: %s", strerror(-r));
+ goto finish;
+ }
+
+ log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+
+ /* 3. Install agent */
+ if ((r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH)) < 0)
+ log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
+ else if (r > 0)
+ log_debug("Installed release agent.");
+ else
+ log_debug("Release agent already installed.");
+
+ /* 4. Realize the group */
+ if ((r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0)) < 0) {
+ log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
+ goto finish;
+ }
+
+ /* 5. And pin it, so that it cannot be unmounted */
+ if (m->pin_cgroupfs_fd >= 0)
+ close_nointr_nofail(m->pin_cgroupfs_fd);
+
+ if ((m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK)) < 0) {
+ log_error("Failed to open pin file: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ log_debug("Created root group.");
+
+finish:
+ free(current);
+ free(path);
+
+ return r;
+}
+
+void manager_shutdown_cgroup(Manager *m, bool delete) {
+ assert(m);
+
+ if (delete && m->cgroup_hierarchy)
+ cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
+
+ if (m->pin_cgroupfs_fd >= 0) {
+ close_nointr_nofail(m->pin_cgroupfs_fd);
+ m->pin_cgroupfs_fd = -1;
+ }
+
+ free(m->cgroup_hierarchy);
+ m->cgroup_hierarchy = NULL;
+}
+
+int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
+ CGroupBonding *b;
+ char *p;
+
+ assert(m);
+ assert(cgroup);
+ assert(bonding);
+
+ b = hashmap_get(m->cgroup_bondings, cgroup);
+ if (b) {
+ *bonding = b;
+ return 1;
+ }
+
+ p = strdup(cgroup);
+ if (!p)
+ return -ENOMEM;
+
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, '/');
+ if (!e || e == p) {
+ free(p);
+ *bonding = NULL;
+ return 0;
+ }
+
+ *e = 0;
+
+ b = hashmap_get(m->cgroup_bondings, p);
+ if (b) {
+ free(p);
+ *bonding = b;
+ return 1;
+ }
+ }
+}
+
+int cgroup_notify_empty(Manager *m, const char *group) {
+ CGroupBonding *l, *b;
+ int r;
+
+ assert(m);
+ assert(group);
+
+ r = cgroup_bonding_get(m, group, &l);
+ if (r <= 0)
+ return r;
+
+ LIST_FOREACH(by_path, b, l) {
+ int t;
+
+ if (!b->unit)
+ continue;
+
+ t = cgroup_bonding_is_empty_list(b);
+ if (t < 0) {
+
+ /* If we don't know, we don't know */
+ if (t != -EAGAIN)
+ log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
+
+ continue;
+ }
+
+ if (t > 0) {
+ /* If it is empty, let's delete it */
+ cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
+
+ if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
+ UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
+ }
+ }
+
+ return 0;
+}
+
+Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
+ CGroupBonding *l, *b;
+ char *group = NULL;
+
+ assert(m);
+
+ if (pid <= 1)
+ return NULL;
+
+ if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
+ return NULL;
+
+ l = hashmap_get(m->cgroup_bondings, group);
+
+ if (!l) {
+ char *slash;
+
+ while ((slash = strrchr(group, '/'))) {
+ if (slash == group)
+ break;
+
+ *slash = 0;
+
+ if ((l = hashmap_get(m->cgroup_bondings, group)))
+ break;
+ }
+ }
+
+ free(group);
+
+ LIST_FOREACH(by_path, b, l) {
+
+ if (!b->unit)
+ continue;
+
+ if (b->ours)
+ return b->unit;
+ }
+
+ return NULL;
+}
+
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
+ CGroupBonding *b;
+
+ assert(controller);
+
+ LIST_FOREACH(by_unit, b, first)
+ if (streq(b->controller, controller))
+ return b;
+
+ return NULL;
+}
+
+char *cgroup_bonding_to_string(CGroupBonding *b) {
+ char *r;
+
+ assert(b);
+
+ if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
+ return NULL;
+
+ return r;
+}
+
+pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
+ FILE *f;
+ pid_t pid = 0, npid, mypid;
+
+ assert(b);
+
+ if (!b->ours)
+ return 0;
+
+ if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
+ return 0;
+
+ mypid = getpid();
+
+ while (cg_read_pid(f, &npid) > 0) {
+ pid_t ppid;
+
+ if (npid == pid)
+ continue;
+
+ /* Ignore processes that aren't our kids */
+ if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
+ continue;
+
+ if (pid != 0) {
+ /* Dang, there's more than one daemonized PID
+ in this group, so we don't know what process
+ is the main process. */
+ pid = 0;
+ break;
+ }
+
+ pid = npid;
+ }
+
+ fclose(f);
+
+ return pid;
+}
+
+pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
+ CGroupBonding *b;
+ pid_t pid;
+
+ /* Try to find a main pid from this cgroup, but checking if
+ * there's only one PID in the cgroup and returning it. Later
+ * on we might want to add additional, smarter heuristics
+ * here. */
+
+ LIST_FOREACH(by_unit, b, first)
+ if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
+ return pid;
+
+ return 0;
+
+}
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
new file mode 100644
index 0000000..5faa7dc
--- /dev/null
+++ b/src/core/cgroup.h
@@ -0,0 +1,94 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocgrouphfoo
+#define foocgrouphfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct CGroupBonding CGroupBonding;
+
+#include "unit.h"
+
+/* Binds a cgroup to a name */
+struct CGroupBonding {
+ char *controller;
+ char *path;
+
+ Unit *unit;
+
+ /* For the Unit::cgroup_bondings list */
+ LIST_FIELDS(CGroupBonding, by_unit);
+
+ /* For the Manager::cgroup_bondings hashmap */
+ LIST_FIELDS(CGroupBonding, by_path);
+
+ /* When shutting down, remove cgroup? Are our own tasks the
+ * only ones in this group?*/
+ bool ours:1;
+
+ /* If we cannot create this group, or add a process to it, is this fatal? */
+ bool essential:1;
+
+ /* This cgroup is realized */
+ bool realized:1;
+};
+
+int cgroup_bonding_realize(CGroupBonding *b);
+int cgroup_bonding_realize_list(CGroupBonding *first);
+
+void cgroup_bonding_free(CGroupBonding *b, bool trim);
+void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
+
+int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
+int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
+
+int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
+int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
+
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
+int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
+
+int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s);
+int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s);
+
+void cgroup_bonding_trim(CGroupBonding *first, bool delete_root);
+void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);
+
+int cgroup_bonding_is_empty(CGroupBonding *b);
+int cgroup_bonding_is_empty_list(CGroupBonding *first);
+
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
+
+char *cgroup_bonding_to_string(CGroupBonding *b);
+
+pid_t cgroup_bonding_search_main_pid(CGroupBonding *b);
+pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b);
+
+#include "manager.h"
+
+int manager_setup_cgroup(Manager *m);
+void manager_shutdown_cgroup(Manager *m, bool delete);
+
+int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding);
+int cgroup_notify_empty(Manager *m, const char *group);
+
+Unit* cgroup_unit_by_pid(Manager *m, pid_t pid);
+
+#endif
diff --git a/src/core/condition.c b/src/core/condition.c
new file mode 100644
index 0000000..2b51a16
--- /dev/null
+++ b/src/core/condition.c
@@ -0,0 +1,323 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/capability.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include "util.h"
+#include "condition.h"
+#include "virt.h"
+
+Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
+ Condition *c;
+
+ assert(type < _CONDITION_TYPE_MAX);
+
+ c = new0(Condition, 1);
+ if (!c)
+ return NULL;
+
+ c->type = type;
+ c->trigger = trigger;
+ c->negate = negate;
+
+ if (parameter) {
+ c->parameter = strdup(parameter);
+ if (!c->parameter) {
+ free(c);
+ return NULL;
+ }
+ }
+
+ return c;
+}
+
+void condition_free(Condition *c) {
+ assert(c);
+
+ free(c->parameter);
+ free(c);
+}
+
+void condition_free_list(Condition *first) {
+ Condition *c, *n;
+
+ LIST_FOREACH_SAFE(conditions, c, n, first)
+ condition_free(c);
+}
+
+static bool test_kernel_command_line(const char *parameter) {
+ char *line, *w, *state, *word = NULL;
+ bool equal;
+ int r;
+ size_t l, pl;
+ bool found = false;
+
+ assert(parameter);
+
+ if (detect_container(NULL) > 0)
+ return false;
+
+ r = read_one_line_file("/proc/cmdline", &line);
+ if (r < 0) {
+ log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+ return false;
+ }
+
+ equal = !!strchr(parameter, '=');
+ pl = strlen(parameter);
+
+ FOREACH_WORD_QUOTED(w, l, line, state) {
+
+ free(word);
+ word = strndup(w, l);
+ if (!word)
+ break;
+
+ if (equal) {
+ if (streq(word, parameter)) {
+ found = true;
+ break;
+ }
+ } else {
+ if (startswith(word, parameter) && (word[pl] == '=' || word[pl] == 0)) {
+ found = true;
+ break;
+ }
+ }
+
+ }
+
+ free(word);
+ free(line);
+
+ return found;
+}
+
+static bool test_virtualization(const char *parameter) {
+ int b;
+ Virtualization v;
+ const char *id;
+
+ assert(parameter);
+
+ v = detect_virtualization(&id);
+ if (v < 0) {
+ log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v));
+ return false;
+ }
+
+ /* First, compare with yes/no */
+ b = parse_boolean(parameter);
+
+ if (v > 0 && b > 0)
+ return true;
+
+ if (v == 0 && b == 0)
+ return true;
+
+ /* Then, compare categorization */
+ if (v == VIRTUALIZATION_VM && streq(parameter, "vm"))
+ return true;
+
+ if (v == VIRTUALIZATION_CONTAINER && streq(parameter, "container"))
+ return true;
+
+ /* Finally compare id */
+ return v > 0 && streq(parameter, id);
+}
+
+static bool test_security(const char *parameter) {
+#ifdef HAVE_SELINUX
+ if (streq(parameter, "selinux"))
+ return is_selinux_enabled() > 0;
+#endif
+ return false;
+}
+
+static bool test_capability(const char *parameter) {
+ cap_value_t value;
+ FILE *f;
+ char line[LINE_MAX];
+ unsigned long long capabilities = (unsigned long long) -1;
+
+ /* If it's an invalid capability, we don't have it */
+
+ if (cap_from_name(parameter, &value) < 0)
+ return false;
+
+ /* If it's a valid capability we default to assume
+ * that we have it */
+
+ f = fopen("/proc/self/status", "re");
+ if (!f)
+ return true;
+
+ while (fgets(line, sizeof(line), f)) {
+ truncate_nl(line);
+
+ if (startswith(line, "CapBnd:")) {
+ (void) sscanf(line+7, "%llx", &capabilities);
+ break;
+ }
+ }
+
+ fclose(f);
+
+ return !!(capabilities & (1ULL << value));
+}
+
+bool condition_test(Condition *c) {
+ assert(c);
+
+ switch(c->type) {
+
+ case CONDITION_PATH_EXISTS:
+ return (access(c->parameter, F_OK) >= 0) == !c->negate;
+
+ case CONDITION_PATH_EXISTS_GLOB:
+ return (glob_exists(c->parameter) > 0) == !c->negate;
+
+ case CONDITION_PATH_IS_DIRECTORY: {
+ struct stat st;
+
+ if (stat(c->parameter, &st) < 0)
+ return c->negate;
+ return S_ISDIR(st.st_mode) == !c->negate;
+ }
+
+ case CONDITION_PATH_IS_SYMBOLIC_LINK: {
+ struct stat st;
+
+ if (lstat(c->parameter, &st) < 0)
+ return c->negate;
+ return S_ISLNK(st.st_mode) == !c->negate;
+ }
+
+ case CONDITION_PATH_IS_MOUNT_POINT:
+ return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;
+
+ case CONDITION_DIRECTORY_NOT_EMPTY: {
+ int k;
+
+ k = dir_is_empty(c->parameter);
+ return !(k == -ENOENT || k > 0) == !c->negate;
+ }
+
+ case CONDITION_FILE_IS_EXECUTABLE: {
+ struct stat st;
+
+ if (stat(c->parameter, &st) < 0)
+ return c->negate;
+
+ return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
+ }
+
+ case CONDITION_KERNEL_COMMAND_LINE:
+ return test_kernel_command_line(c->parameter) == !c->negate;
+
+ case CONDITION_VIRTUALIZATION:
+ return test_virtualization(c->parameter) == !c->negate;
+
+ case CONDITION_SECURITY:
+ return test_security(c->parameter) == !c->negate;
+
+ case CONDITION_CAPABILITY:
+ return test_capability(c->parameter) == !c->negate;
+
+ case CONDITION_NULL:
+ return !c->negate;
+
+ default:
+ assert_not_reached("Invalid condition type.");
+ }
+}
+
+bool condition_test_list(Condition *first) {
+ Condition *c;
+ int triggered = -1;
+
+ /* If the condition list is empty, then it is true */
+ if (!first)
+ return true;
+
+ /* Otherwise, if all of the non-trigger conditions apply and
+ * if any of the trigger conditions apply (unless there are
+ * none) we return true */
+ LIST_FOREACH(conditions, c, first) {
+ bool b;
+
+ b = condition_test(c);
+
+ if (!c->trigger && !b)
+ return false;
+
+ if (c->trigger && triggered <= 0)
+ triggered = b;
+ }
+
+ return triggered != 0;
+}
+
+void condition_dump(Condition *c, FILE *f, const char *prefix) {
+ assert(c);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ fprintf(f,
+ "%s\t%s: %s%s%s\n",
+ prefix,
+ condition_type_to_string(c->type),
+ c->trigger ? "|" : "",
+ c->negate ? "!" : "",
+ c->parameter);
+}
+
+void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
+ Condition *c;
+
+ LIST_FOREACH(conditions, c, first)
+ condition_dump(c, f, prefix);
+}
+
+static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
+ [CONDITION_PATH_EXISTS] = "ConditionPathExists",
+ [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
+ [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
+ [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
+ [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
+ [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
+ [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
+ [CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
+ [CONDITION_SECURITY] = "ConditionSecurity",
+ [CONDITION_NULL] = "ConditionNull"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
diff --git a/src/core/condition.h b/src/core/condition.h
new file mode 100644
index 0000000..71b1c67
--- /dev/null
+++ b/src/core/condition.h
@@ -0,0 +1,69 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooconditionhfoo
+#define fooconditionhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "list.h"
+
+typedef enum ConditionType {
+ CONDITION_PATH_EXISTS,
+ CONDITION_PATH_EXISTS_GLOB,
+ CONDITION_PATH_IS_DIRECTORY,
+ CONDITION_PATH_IS_SYMBOLIC_LINK,
+ CONDITION_PATH_IS_MOUNT_POINT,
+ CONDITION_DIRECTORY_NOT_EMPTY,
+ CONDITION_FILE_IS_EXECUTABLE,
+ CONDITION_KERNEL_COMMAND_LINE,
+ CONDITION_VIRTUALIZATION,
+ CONDITION_SECURITY,
+ CONDITION_CAPABILITY,
+ CONDITION_NULL,
+ _CONDITION_TYPE_MAX,
+ _CONDITION_TYPE_INVALID = -1
+} ConditionType;
+
+typedef struct Condition {
+ ConditionType type;
+ char *parameter;
+
+ bool trigger:1;
+ bool negate:1;
+
+ LIST_FIELDS(struct Condition, conditions);
+} Condition;
+
+Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate);
+void condition_free(Condition *c);
+void condition_free_list(Condition *c);
+
+bool condition_test(Condition *c);
+bool condition_test_list(Condition *c);
+
+void condition_dump(Condition *c, FILE *f, const char *prefix);
+void condition_dump_list(Condition *c, FILE *f, const char *prefix);
+
+const char* condition_type_to_string(ConditionType t);
+int condition_type_from_string(const char *s);
+
+#endif
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
new file mode 100644
index 0000000..8e45f81
--- /dev/null
+++ b/src/core/dbus-automount.c
@@ -0,0 +1,72 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-automount.h"
+#include "dbus-common.h"
+
+#define BUS_AUTOMOUNT_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Automount\">\n" \
+ " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_AUTOMOUNT_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Automount\0"
+
+const char bus_automount_interface[] _introspect_("Automount") = BUS_AUTOMOUNT_INTERFACE;
+
+const char bus_automount_invalidating_properties[] =
+ "Result\0";
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_automount_append_automount_result, automount_result, AutomountResult);
+
+static const BusProperty bus_automount_properties[] = {
+ { "Where", bus_property_append_string, "s", offsetof(Automount, where), true },
+ { "DirectoryMode", bus_property_append_mode, "u", offsetof(Automount, directory_mode) },
+ { "Result", bus_automount_append_automount_result, "s", offsetof(Automount, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Automount *am = AUTOMOUNT(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Automount", bus_automount_properties, am },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h
new file mode 100644
index 0000000..2fc8345
--- /dev/null
+++ b/src/core/dbus-automount.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusautomounthfoo
+#define foodbusautomounthfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_automount_interface[];
+extern const char bus_automount_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c
new file mode 100644
index 0000000..b39fb9d
--- /dev/null
+++ b/src/core/dbus-device.c
@@ -0,0 +1,65 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "dbus-unit.h"
+#include "dbus-device.h"
+#include "dbus-common.h"
+
+#define BUS_DEVICE_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Device\">\n" \
+ " <property name=\"SysFSPath\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_DEVICE_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Device\0"
+
+const char bus_device_interface[] _introspect_("Device") = BUS_DEVICE_INTERFACE;
+
+const char bus_device_invalidating_properties[] =
+ "SysFSPath\0";
+
+static const BusProperty bus_device_properties[] = {
+ { "SysFSPath", bus_property_append_string, "s", offsetof(Device, sysfs), true },
+ { NULL, }
+};
+
+
+DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Device *d = DEVICE(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Device", bus_device_properties, d },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-device.h b/src/core/dbus-device.h
new file mode 100644
index 0000000..fba270b
--- /dev/null
+++ b/src/core/dbus-device.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusdevicehfoo
+#define foodbusdevicehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_device_interface[];
+extern const char bus_device_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
new file mode 100644
index 0000000..1fd2b21
--- /dev/null
+++ b/src/core/dbus-execute.c
@@ -0,0 +1,422 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <dbus/dbus.h>
+#include <sys/prctl.h>
+
+#include "dbus-execute.h"
+#include "missing.h"
+#include "ioprio.h"
+#include "strv.h"
+#include "dbus-common.h"
+
+DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_kill_mode, kill_mode, KillMode);
+
+DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
+DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
+
+int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data) {
+ char **env_files = data, **j;
+ DBusMessageIter sub, sub2;
+
+ assert(i);
+ assert(property);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sb)", &sub))
+ return -ENOMEM;
+
+ STRV_FOREACH(j, env_files) {
+ dbus_bool_t b = false;
+ char *fn = *j;
+
+ if (fn[0] == '-') {
+ b = true;
+ fn++;
+ }
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &fn) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int32_t n;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->oom_score_adjust_set)
+ n = c->oom_score_adjust;
+ else {
+ char *t;
+
+ n = 0;
+ if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) {
+ safe_atoi(t, &n);
+ free(t);
+ } else if (read_one_line_file("/proc/self/oom_adj", &t) >= 0) {
+ safe_atoi(t, &n);
+ free(t);
+
+ if (n == OOM_ADJUST_MAX)
+ n = OOM_SCORE_ADJ_MAX;
+ else
+ n = (n * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+ }
+ }
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int32_t n;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->nice_set)
+ n = c->nice;
+ else
+ n = getpriority(PRIO_PROCESS, 0);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int32_t n;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->ioprio_set)
+ n = c->ioprio;
+ else
+ n = ioprio_get(IOPRIO_WHO_PROCESS, 0);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int32_t n;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->cpu_sched_set)
+ n = c->cpu_sched_policy;
+ else
+ n = sched_getscheduler(0);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int32_t n;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->cpu_sched_set)
+ n = c->cpu_sched_priority;
+ else {
+ struct sched_param p;
+ n = 0;
+
+ zero(p);
+ if (sched_getparam(0, &p) >= 0)
+ n = p.sched_priority;
+ }
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ dbus_bool_t b;
+ DBusMessageIter sub;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub))
+ return -ENOMEM;
+
+ if (c->cpuset)
+ b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus));
+ else
+ b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, 0);
+
+ if (!b)
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ uint64_t u;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->timer_slack_nsec_set)
+ u = (uint64_t) c->timer_slack_nsec;
+ else
+ u = (uint64_t) prctl(PR_GET_TIMERSLACK);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ uint64_t normal, inverted;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ /* We store this negated internally, to match the kernel, but
+ * we expose it normalized. */
+
+ normal = *(uint64_t*) data;
+ inverted = ~normal;
+
+ return bus_property_append_uint64(i, property, &inverted);
+}
+
+int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ char *t = NULL;
+ const char *s;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (c->capabilities)
+ s = t = cap_to_text(c->capabilities, NULL);
+ else
+ s = "";
+
+ if (!s)
+ return -ENOMEM;
+
+ b = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &s);
+
+ if (t)
+ cap_free(t);
+
+ if (!b)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data) {
+ ExecContext *c = data;
+ int r;
+ uint64_t u;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ assert_se((r = rlimit_from_string(property)) >= 0);
+
+ if (c->rlimit[r])
+ u = (uint64_t) c->rlimit[r]->rlim_max;
+ else {
+ struct rlimit rl;
+
+ zero(rl);
+ getrlimit(r, &rl);
+
+ u = (uint64_t) rl.rlim_max;
+ }
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int bus_execute_append_command(DBusMessageIter *i, const char *property, void *data) {
+ ExecCommand *c = data;
+ DBusMessageIter sub, sub2, sub3;
+
+ assert(i);
+ assert(property);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sasbttttuii)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(command, c, c) {
+ char **l;
+ uint32_t pid;
+ int32_t code, status;
+ dbus_bool_t b;
+
+ if (!c->path)
+ continue;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &c->path) ||
+ !dbus_message_iter_open_container(&sub2, DBUS_TYPE_ARRAY, "s", &sub3))
+ return -ENOMEM;
+
+ STRV_FOREACH(l, c->argv)
+ if (!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, l))
+ return -ENOMEM;
+
+ pid = (uint32_t) c->exec_status.pid;
+ code = (int32_t) c->exec_status.code;
+ status = (int32_t) c->exec_status.status;
+
+ b = !!c->ignore;
+
+ if (!dbus_message_iter_close_container(&sub2, &sub3) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.realtime) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.monotonic) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.realtime) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.monotonic) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &code) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &status))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+const BusProperty bus_exec_context_properties[] = {
+ { "Environment", bus_property_append_strv, "as", offsetof(ExecContext, environment), true },
+ { "EnvironmentFiles", bus_execute_append_env_files, "a(sb)", offsetof(ExecContext, environment_files), true },
+ { "UMask", bus_property_append_mode, "u", offsetof(ExecContext, umask) },
+ { "LimitCPU", bus_execute_append_rlimits, "t", 0 },
+ { "LimitFSIZE", bus_execute_append_rlimits, "t", 0 },
+ { "LimitDATA", bus_execute_append_rlimits, "t", 0 },
+ { "LimitSTACK", bus_execute_append_rlimits, "t", 0 },
+ { "LimitCORE", bus_execute_append_rlimits, "t", 0 },
+ { "LimitRSS", bus_execute_append_rlimits, "t", 0 },
+ { "LimitNOFILE", bus_execute_append_rlimits, "t", 0 },
+ { "LimitAS", bus_execute_append_rlimits, "t", 0 },
+ { "LimitNPROC", bus_execute_append_rlimits, "t", 0 },
+ { "LimitMEMLOCK", bus_execute_append_rlimits, "t", 0 },
+ { "LimitLOCKS", bus_execute_append_rlimits, "t", 0 },
+ { "LimitSIGPENDING", bus_execute_append_rlimits, "t", 0 },
+ { "LimitMSGQUEUE", bus_execute_append_rlimits, "t", 0 },
+ { "LimitNICE", bus_execute_append_rlimits, "t", 0 },
+ { "LimitRTPRIO", bus_execute_append_rlimits, "t", 0 },
+ { "LimitRTTIME", bus_execute_append_rlimits, "t", 0 },
+ { "WorkingDirectory", bus_property_append_string, "s", offsetof(ExecContext, working_directory), true },
+ { "RootDirectory", bus_property_append_string, "s", offsetof(ExecContext, root_directory), true },
+ { "OOMScoreAdjust", bus_execute_append_oom_score_adjust, "i", 0 },
+ { "Nice", bus_execute_append_nice, "i", 0 },
+ { "IOScheduling", bus_execute_append_ioprio, "i", 0 },
+ { "CPUSchedulingPolicy", bus_execute_append_cpu_sched_policy, "i", 0 },
+ { "CPUSchedulingPriority", bus_execute_append_cpu_sched_priority, "i", 0 },
+ { "CPUAffinity", bus_execute_append_affinity, "ay", 0 },
+ { "TimerSlackNSec", bus_execute_append_timer_slack_nsec, "t", 0 },
+ { "CPUSchedulingResetOnFork", bus_property_append_bool, "b", offsetof(ExecContext, cpu_sched_reset_on_fork) },
+ { "NonBlocking", bus_property_append_bool, "b", offsetof(ExecContext, non_blocking) },
+ { "StandardInput", bus_execute_append_input, "s", offsetof(ExecContext, std_input) },
+ { "StandardOutput", bus_execute_append_output, "s", offsetof(ExecContext, std_output) },
+ { "StandardError", bus_execute_append_output, "s", offsetof(ExecContext, std_error) },
+ { "TTYPath", bus_property_append_string, "s", offsetof(ExecContext, tty_path), true },
+ { "TTYReset", bus_property_append_bool, "b", offsetof(ExecContext, tty_reset) },
+ { "TTYVHangup", bus_property_append_bool, "b", offsetof(ExecContext, tty_vhangup) },
+ { "TTYVTDisallocate", bus_property_append_bool, "b", offsetof(ExecContext, tty_vt_disallocate) },
+ { "SyslogPriority", bus_property_append_int, "i", offsetof(ExecContext, syslog_priority) },
+ { "SyslogIdentifier", bus_property_append_string, "s", offsetof(ExecContext, syslog_identifier), true },
+ { "SyslogLevelPrefix", bus_property_append_bool, "b", offsetof(ExecContext, syslog_level_prefix) },
+ { "Capabilities", bus_execute_append_capabilities, "s", 0 },
+ { "SecureBits", bus_property_append_int, "i", offsetof(ExecContext, secure_bits) },
+ { "CapabilityBoundingSet", bus_execute_append_capability_bs, "t", offsetof(ExecContext, capability_bounding_set_drop) },
+ { "User", bus_property_append_string, "s", offsetof(ExecContext, user), true },
+ { "Group", bus_property_append_string, "s", offsetof(ExecContext, group), true },
+ { "SupplementaryGroups", bus_property_append_strv, "as", offsetof(ExecContext, supplementary_groups), true },
+ { "TCPWrapName", bus_property_append_string, "s", offsetof(ExecContext, tcpwrap_name), true },
+ { "PAMName", bus_property_append_string, "s", offsetof(ExecContext, pam_name), true },
+ { "ReadWriteDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_write_dirs), true },
+ { "ReadOnlyDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_only_dirs), true },
+ { "InaccessibleDirectories", bus_property_append_strv, "as", offsetof(ExecContext, inaccessible_dirs), true },
+ { "MountFlags", bus_property_append_ul, "t", offsetof(ExecContext, mount_flags) },
+ { "PrivateTmp", bus_property_append_bool, "b", offsetof(ExecContext, private_tmp) },
+ { "PrivateNetwork", bus_property_append_bool, "b", offsetof(ExecContext, private_network) },
+ { "SameProcessGroup", bus_property_append_bool, "b", offsetof(ExecContext, same_pgrp) },
+ { "KillMode", bus_execute_append_kill_mode, "s", offsetof(ExecContext, kill_mode) },
+ { "KillSignal", bus_property_append_int, "i", offsetof(ExecContext, kill_signal) },
+ { "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true },
+ { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) },
+ { "ControlGroupPersistent", bus_property_append_tristate_false, "b", offsetof(ExecContext, control_group_persistent) },
+ { "IgnoreSIGPIPE", bus_property_append_bool, "b", offsetof(ExecContext, ignore_sigpipe ) },
+ { NULL, }
+};
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
new file mode 100644
index 0000000..03cd69d
--- /dev/null
+++ b/src/core/dbus-execute.h
@@ -0,0 +1,125 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusexecutehfoo
+#define foodbusexecutehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "manager.h"
+#include "dbus-common.h"
+
+#define BUS_EXEC_STATUS_INTERFACE(prefix) \
+ " <property name=\"" prefix "StartTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "StartTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "ExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "ExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "PID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "Code\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"" prefix "Status\" type=\"i\" access=\"read\"/>\n"
+
+#define BUS_EXEC_CONTEXT_INTERFACE \
+ " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitDATA\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitSTACK\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitCORE\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitRSS\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitNOFILE\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitAS\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitNPROC\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitMEMLOCK\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitLOCKS\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitSIGPENDING\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitMSGQUEUE\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitNICE\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitRTPRIO\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LimitRTTIME\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"WorkingDirectory\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"OOMScoreAdjust\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"Nice\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"IOScheduling\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"CPUSchedulingPolicy\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"CPUSchedulingPriority\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"CPUAffinity\" type=\"ay\" access=\"read\"/>\n" \
+ " <property name=\"TimerSlackNS\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"CPUSchedulingResetOnFork\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"NonBlocking\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"StandardInput\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"StandardOutput\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"StandardError\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"TTYPath\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"TTYReset\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"TTYVHangup\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"TTYVTDisallocate\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"SyslogPriority\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"SyslogIdentifier\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"SyslogLevelPrefix\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Capabilities\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"SecureBits\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"CapabilityBoundingSet\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"User\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Group\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"SupplementaryGroups\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"TCPWrapName\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"PAMName\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ReadWriteDirectories\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ReadOnlyDirectories\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n"
+
+#define BUS_EXEC_COMMAND_INTERFACE(name) \
+ " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n"
+
+extern const BusProperty bus_exec_context_properties[];
+
+#define BUS_EXEC_COMMAND_PROPERTY(name, command, indirect) \
+ { name, bus_execute_append_command, "a(sasbttttuii)", (command), (indirect), NULL }
+
+int bus_execute_append_output(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_input(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_command(DBusMessageIter *u, const char *property, void *data);
+int bus_execute_append_kill_mode(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data);
+
+#endif
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
new file mode 100644
index 0000000..ab6d610
--- /dev/null
+++ b/src/core/dbus-job.c
@@ -0,0 +1,354 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "dbus-job.h"
+#include "dbus-common.h"
+
+#define BUS_JOB_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
+ " <method name=\"Cancel\"/>\n" \
+ " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
+ " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_JOB_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
+
+#define INTERFACES_LIST \
+ BUS_GENERIC_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Job\0"
+
+#define INVALIDATING_PROPERTIES \
+ "State\0"
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
+
+static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
+ Job *j = data;
+ DBusMessageIter sub;
+ char *p;
+
+ assert(i);
+ assert(property);
+ assert(j);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+ return -ENOMEM;
+
+ if (!(p = unit_dbus_path(j->unit)))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ free(p);
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const BusProperty bus_job_properties[] = {
+ { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
+ { "State", bus_job_append_state, "s", offsetof(Job, state) },
+ { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
+ { "Unit", bus_job_append_unit, "(so)", 0 },
+ { NULL, }
+};
+
+static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
+ DBusMessage *reply = NULL;
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ job_finish_and_invalidate(j, JOB_CANCELED);
+
+ } else {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Job", bus_job_properties, j },
+ { NULL, }
+ };
+ return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
+ }
+
+ if (reply) {
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ Job *j;
+ int r;
+ DBusMessage *reply;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
+ /* Be nice to gdbus and return introspection data for our mid-level paths */
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+ char *introspection = NULL;
+ FILE *f;
+ Iterator i;
+ size_t size;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ /* We roll our own introspection code here, instead of
+ * relying on bus_default_message_handler() because we
+ * need to generate our introspection string
+ * dynamically. */
+
+ if (!(f = open_memstream(&introspection, &size)))
+ goto oom;
+
+ fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node>\n", f);
+
+ fputs(BUS_INTROSPECTABLE_INTERFACE, f);
+ fputs(BUS_PEER_INTERFACE, f);
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
+
+ fputs("</node>\n", f);
+
+ if (ferror(f)) {
+ fclose(f);
+ free(introspection);
+ goto oom;
+ }
+
+ fclose(f);
+
+ if (!introspection)
+ goto oom;
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
+ free(introspection);
+ goto oom;
+ }
+
+ free(introspection);
+
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
+
+ if (r == -ENOMEM)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (r == -ENOENT) {
+ DBusError e;
+
+ dbus_error_init(&e);
+ dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
+ return bus_send_error_reply(connection, message, &e, r);
+ }
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ return bus_job_message_dispatch(j, connection, message);
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+const DBusObjectPathVTable bus_job_vtable = {
+ .message_function = bus_job_message_handler
+};
+
+static int job_send_message(Job *j, DBusMessage *m) {
+ int r;
+
+ assert(j);
+ assert(m);
+
+ if (bus_has_subscriber(j->manager)) {
+ if ((r = bus_broadcast(j->manager, m)) < 0)
+ return r;
+
+ } else if (j->bus_client) {
+ /* If nobody is subscribed, we just send the message
+ * to the client which created the job */
+
+ assert(j->bus);
+
+ if (!dbus_message_set_destination(m, j->bus_client))
+ return -ENOMEM;
+
+ if (!dbus_connection_send(j->bus, m, NULL))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void bus_job_send_change_signal(Job *j) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(j);
+
+ if (j->in_dbus_queue) {
+ LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = false;
+ }
+
+ if (!bus_has_subscriber(j->manager) && !j->bus_client) {
+ j->sent_dbus_new_signal = true;
+ return;
+ }
+
+ if (!(p = job_dbus_path(j)))
+ goto oom;
+
+ if (j->sent_dbus_new_signal) {
+ /* Send a properties changed signal */
+
+ if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES)))
+ goto oom;
+
+ } else {
+ /* Send a new signal */
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew")))
+ goto oom;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &j->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (job_send_message(j, m) < 0)
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ j->sent_dbus_new_signal = true;
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate job change signal.");
+}
+
+void bus_job_send_removed_signal(Job *j) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+ const char *r;
+
+ assert(j);
+
+ if (!bus_has_subscriber(j->manager) && !j->bus_client)
+ return;
+
+ if (!j->sent_dbus_new_signal)
+ bus_job_send_change_signal(j);
+
+ if (!(p = job_dbus_path(j)))
+ goto oom;
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved")))
+ goto oom;
+
+ r = job_result_to_string(j->result);
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &j->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_STRING, &r,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (job_send_message(j, m) < 0)
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate job remove signal.");
+}
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
new file mode 100644
index 0000000..103c2ff
--- /dev/null
+++ b/src/core/dbus-job.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusjobhfoo
+#define foodbusjobhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "job.h"
+
+void bus_job_send_change_signal(Job *j);
+void bus_job_send_removed_signal(Job *j);
+
+extern const DBusObjectPathVTable bus_job_vtable;
+
+extern const char bus_job_interface[];
+
+#endif
diff --git a/src/core/dbus-loop.h b/src/core/dbus-loop.h
new file mode 100644
index 0000000..0bbdfe5
--- /dev/null
+++ b/src/core/dbus-loop.h
@@ -0,0 +1,30 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusloophfoo
+#define foodbusloophfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+int bus_loop_open(DBusConnection *c);
+int bus_loop_dispatch(int fd);
+
+#endif
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
new file mode 100644
index 0000000..3bf0c07
--- /dev/null
+++ b/src/core/dbus-manager.c
@@ -0,0 +1,1544 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <unistd.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "dbus-manager.h"
+#include "strv.h"
+#include "bus-errors.h"
+#include "build.h"
+#include "dbus-common.h"
+#include "install.h"
+
+#define BUS_MANAGER_INTERFACE_BEGIN \
+ " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
+
+#define BUS_MANAGER_INTERFACE_METHODS \
+ " <method name=\"GetUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetUnitByPID\">\n" \
+ " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"LoadUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"StartUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"StartUnitReplace\">\n" \
+ " <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"StopUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReloadUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"RestartUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"TryRestartUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReloadOrRestartUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReloadOrTryRestartUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"KillUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ResetFailedUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetJob\">\n" \
+ " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ClearJobs\"/>\n" \
+ " <method name=\"ResetFailed\"/>\n" \
+ " <method name=\"ListUnits\">\n" \
+ " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ListJobs\">\n" \
+ " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Subscribe\"/>\n" \
+ " <method name=\"Unsubscribe\"/>\n" \
+ " <method name=\"Dump\"/>\n" \
+ " <method name=\"CreateSnapshot\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Reload\"/>\n" \
+ " <method name=\"Reexecute\"/>\n" \
+ " <method name=\"Exit\"/>\n" \
+ " <method name=\"Reboot\"/>\n" \
+ " <method name=\"PowerOff\"/>\n" \
+ " <method name=\"Halt\"/>\n" \
+ " <method name=\"KExec\"/>\n" \
+ " <method name=\"SetEnvironment\">\n" \
+ " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetEnvironment\">\n" \
+ " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetAndSetEnvironment\">\n" \
+ " <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"set\" type=\"as\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ListUnitFiles\">\n" \
+ " <arg name=\"changes\" type=\"a(ss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetUnitFileState\">\n" \
+ " <arg name=\"file\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"state\" type=\"s\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"EnableUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"DisableUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReenableUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"LinkUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"PresetUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"MaskUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnmaskUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n"
+
+#define BUS_MANAGER_INTERFACE_SIGNALS \
+ " <signal name=\"UnitNew\">\n" \
+ " <arg name=\"id\" type=\"s\"/>\n" \
+ " <arg name=\"unit\" type=\"o\"/>\n" \
+ " </signal>\n" \
+ " <signal name=\"UnitRemoved\">\n" \
+ " <arg name=\"id\" type=\"s\"/>\n" \
+ " <arg name=\"unit\" type=\"o\"/>\n" \
+ " </signal>\n" \
+ " <signal name=\"JobNew\">\n" \
+ " <arg name=\"id\" type=\"u\"/>\n" \
+ " <arg name=\"job\" type=\"o\"/>\n" \
+ " </signal>\n" \
+ " <signal name=\"JobRemoved\">\n" \
+ " <arg name=\"id\" type=\"u\"/>\n" \
+ " <arg name=\"job\" type=\"o\"/>\n" \
+ " <arg name=\"result\" type=\"s\"/>\n" \
+ " </signal>" \
+ " <signal name=\"StartupFinished\">\n" \
+ " <arg name=\"kernel\" type=\"t\"/>\n" \
+ " <arg name=\"initrd\" type=\"t\"/>\n" \
+ " <arg name=\"userspace\" type=\"t\"/>\n" \
+ " <arg name=\"total\" type=\"t\"/>\n" \
+ " </signal>" \
+ " <signal name=\"UnitFilesChanged\"/>\n"
+
+#define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
+ " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Features\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"RunningAs\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"StartupTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \
+ " <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
+ " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Progress\" type=\"d\" access=\"read\"/>\n" \
+ " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"NotifySocket\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"MountAuto\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"SwapAuto\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"read\"/>\n"
+
+#ifdef HAVE_SYSV_COMPAT
+#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
+ " <property name=\"SysVConsole\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"SysVInitPath\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"SysVRcndPath\" type=\"as\" access=\"read\"/>\n"
+#else
+#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV
+#endif
+
+#define BUS_MANAGER_INTERFACE_END \
+ " </interface>\n"
+
+#define BUS_MANAGER_INTERFACE \
+ BUS_MANAGER_INTERFACE_BEGIN \
+ BUS_MANAGER_INTERFACE_METHODS \
+ BUS_MANAGER_INTERFACE_SIGNALS \
+ BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
+ BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
+ BUS_MANAGER_INTERFACE_END
+
+#define INTROSPECTION_BEGIN \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_MANAGER_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE
+
+#define INTROSPECTION_END \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_GENERIC_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Manager\0"
+
+const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE;
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput);
+
+static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) {
+ const char *t;
+ Manager *m = data;
+ char buf[LINE_MAX] = "", *e = buf, *p = NULL;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (m->taint_usr)
+ e = stpcpy(e, "usr-separate-fs ");
+
+ if (readlink_malloc("/etc/mtab", &p) < 0)
+ e = stpcpy(e, "etc-mtab-not-symlink ");
+ else
+ free(p);
+
+ if (access("/proc/cgroups", F_OK) < 0)
+ stpcpy(e, "cgroups-missing ");
+
+ t = strstrip(buf);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) {
+ const char *t;
+
+ assert(i);
+ assert(property);
+
+ t = log_target_to_string(log_get_target());
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) {
+ const char *t;
+
+ assert(i);
+ assert(property);
+
+ dbus_message_iter_get_basic(i, &t);
+
+ return log_set_target_from_string(t);
+}
+
+static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
+ const char *t;
+
+ assert(i);
+ assert(property);
+
+ t = log_level_to_string(log_get_max_level());
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
+ const char *t;
+
+ assert(i);
+ assert(property);
+
+ dbus_message_iter_get_basic(i, &t);
+
+ return log_set_max_level_from_string(t);
+}
+
+static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) {
+ Manager *m = data;
+ uint32_t u;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ u = hashmap_size(m->units);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) {
+ Manager *m = data;
+ uint32_t u;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ u = hashmap_size(m->jobs);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) {
+ double d;
+ Manager *m = data;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (dual_timestamp_is_set(&m->finish_timestamp))
+ d = 1.0;
+ else
+ d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const char *message_get_sender_with_fallback(DBusMessage *m) {
+ const char *s;
+
+ assert(m);
+
+ if ((s = dbus_message_get_sender(m)))
+ return s;
+
+ /* When the message came in from a direct connection the
+ * message will have no sender. We fix that here. */
+
+ return ":no-sender";
+}
+
+static DBusMessage *message_from_file_changes(
+ DBusMessage *m,
+ UnitFileChange *changes,
+ unsigned n_changes,
+ int carries_install_info) {
+
+ DBusMessageIter iter, sub, sub2;
+ DBusMessage *reply;
+ unsigned i;
+
+ reply = dbus_message_new_method_return(m);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (carries_install_info >= 0) {
+ dbus_bool_t b;
+
+ b = !!carries_install_info;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
+ goto oom;
+ }
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
+ goto oom;
+
+ for (i = 0; i < n_changes; i++) {
+ const char *type, *path, *source;
+
+ type = unit_file_change_type_to_string(changes[i].type);
+ path = strempty(changes[i].path);
+ source = strempty(changes[i].source);
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ goto oom;
+
+ return reply;
+
+oom:
+ dbus_message_unref(reply);
+ return NULL;
+}
+
+static int bus_manager_send_unit_files_changed(Manager *m) {
+ DBusMessage *s;
+ int r;
+
+ s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
+ if (!s)
+ return -ENOMEM;
+
+ r = bus_broadcast(m, s);
+ dbus_message_unref(s);
+
+ return r;
+}
+
+static const char systemd_property_string[] =
+ PACKAGE_STRING "\0"
+ DISTRIBUTION "\0"
+ SYSTEMD_FEATURES;
+
+static const BusProperty bus_systemd_properties[] = {
+ { "Version", bus_property_append_string, "s", 0 },
+ { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) },
+ { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) },
+ { NULL, }
+};
+
+static const BusProperty bus_manager_properties[] = {
+ { "RunningAs", bus_manager_append_running_as, "s", offsetof(Manager, running_as) },
+ { "Tainted", bus_manager_append_tainted, "s", 0 },
+ { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) },
+ { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) },
+ { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) },
+ { "StartupTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.monotonic) },
+ { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
+ { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
+ { "LogLevel", bus_manager_append_log_level, "s", 0, 0, bus_manager_set_log_level },
+ { "LogTarget", bus_manager_append_log_target, "s", 0, 0, bus_manager_set_log_target },
+ { "NNames", bus_manager_append_n_names, "u", 0 },
+ { "NJobs", bus_manager_append_n_jobs, "u", 0 },
+ { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) },
+ { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) },
+ { "Progress", bus_manager_append_progress, "d", 0 },
+ { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true },
+ { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
+ { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
+ { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
+ { "NotifySocket", bus_property_append_string, "s", offsetof(Manager, notify_socket), true },
+ { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
+ { "MountAuto", bus_property_append_bool, "b", offsetof(Manager, mount_auto) },
+ { "SwapAuto", bus_property_append_bool, "b", offsetof(Manager, swap_auto) },
+ { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
+ { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
+ { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
+ { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), },
+ { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), },
+#ifdef HAVE_SYSV_COMPAT
+ { "SysVConsole", bus_property_append_bool, "b", offsetof(Manager, sysv_console) },
+ { "SysVInitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvinit_path), true },
+ { "SysVRcndPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvrcnd_path), true },
+#endif
+ { NULL, }
+};
+
+static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+
+ int r;
+ DBusError error;
+ DBusMessage *reply = NULL;
+ char * path = NULL;
+ JobType job_type = _JOB_TYPE_INVALID;
+ bool reload_if_possible = false;
+ const char *member;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ dbus_error_init(&error);
+
+ member = dbus_message_get_member(message);
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (!(u = manager_get_unit(m, name))) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = unit_dbus_path(u)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
+ Unit *u;
+ uint32_t pid;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = unit_dbus_path(u)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = unit_dbus_path(u)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
+ job_type = JOB_START;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
+ job_type = JOB_START;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
+ job_type = JOB_STOP;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
+ job_type = JOB_RELOAD;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
+ job_type = JOB_RESTART;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
+ job_type = JOB_TRY_RESTART;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
+ reload_if_possible = true;
+ job_type = JOB_RESTART;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
+ reload_if_possible = true;
+ job_type = JOB_TRY_RESTART;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
+ const char *name, *swho, *smode;
+ int32_t signo;
+ Unit *u;
+ KillMode mode;
+ KillWho who;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &swho,
+ DBUS_TYPE_STRING, &smode,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (isempty(swho))
+ who = KILL_ALL;
+ else {
+ who = kill_who_from_string(swho);
+ if (who < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (isempty(smode))
+ mode = KILL_CONTROL_GROUP;
+ else {
+ mode = kill_mode_from_string(smode);
+ if (mode < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (!(u = manager_get_unit(m, name))) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
+ uint32_t id;
+ Job *j;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (!(j = manager_get_job(m, id))) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = job_dbus_path(j)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
+
+ manager_clear_jobs(m);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
+
+ manager_reset_failed(m);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (!(u = manager_get_unit(m, name))) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ unit_reset_failed(u);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
+ DBusMessageIter iter, sub;
+ Iterator i;
+ Unit *u;
+ const char *k;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
+ goto oom;
+
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+ char *u_path, *j_path;
+ const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
+ DBusMessageIter sub2;
+ uint32_t job_id;
+ Unit *f;
+
+ if (k != u->id)
+ continue;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+ goto oom;
+
+ description = unit_description(u);
+ load_state = unit_load_state_to_string(u->load_state);
+ active_state = unit_active_state_to_string(unit_active_state(u));
+ sub_state = unit_sub_state_to_string(u);
+
+ f = unit_following(u);
+ following = f ? f->id : "";
+
+ if (!(u_path = unit_dbus_path(u)))
+ goto oom;
+
+ if (u->job) {
+ job_id = (uint32_t) u->job->id;
+
+ if (!(j_path = job_dbus_path(u->job))) {
+ free(u_path);
+ goto oom;
+ }
+
+ sjob_type = job_type_to_string(u->job->type);
+ } else {
+ job_id = 0;
+ j_path = u_path;
+ sjob_type = "";
+ }
+
+ if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
+ free(u_path);
+ if (u->job)
+ free(j_path);
+ goto oom;
+ }
+
+ free(u_path);
+ if (u->job)
+ free(j_path);
+
+ if (!dbus_message_iter_close_container(&sub, &sub2))
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
+ DBusMessageIter iter, sub;
+ Iterator i;
+ Job *j;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
+ goto oom;
+
+ HASHMAP_FOREACH(j, m->jobs, i) {
+ char *u_path, *j_path;
+ const char *state, *type;
+ uint32_t id;
+ DBusMessageIter sub2;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+ goto oom;
+
+ id = (uint32_t) j->id;
+ state = job_state_to_string(j->state);
+ type = job_type_to_string(j->type);
+
+ if (!(j_path = job_dbus_path(j)))
+ goto oom;
+
+ if (!(u_path = unit_dbus_path(j->unit))) {
+ free(j_path);
+ goto oom;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
+ free(j_path);
+ free(u_path);
+ goto oom;
+ }
+
+ free(j_path);
+ free(u_path);
+
+ if (!dbus_message_iter_close_container(&sub, &sub2))
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
+ char *client;
+ Set *s;
+
+ if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
+ if (!(s = set_new(string_hash_func, string_compare_func)))
+ goto oom;
+
+ if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
+ set_free(s);
+ goto oom;
+ }
+ }
+
+ if (!(client = strdup(message_get_sender_with_fallback(message))))
+ goto oom;
+
+ if ((r = set_put(s, client)) < 0) {
+ free(client);
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
+ char *client;
+
+ if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ free(client);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
+ FILE *f;
+ char *dump = NULL;
+ size_t size;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(f = open_memstream(&dump, &size)))
+ goto oom;
+
+ manager_dump_units(m, f, NULL);
+ manager_dump_jobs(m, f, NULL);
+
+ if (ferror(f)) {
+ fclose(f);
+ free(dump);
+ goto oom;
+ }
+
+ fclose(f);
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
+ free(dump);
+ goto oom;
+ }
+
+ free(dump);
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
+ const char *name;
+ dbus_bool_t cleanup;
+ Snapshot *s;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &cleanup,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (name && name[0] == 0)
+ name = NULL;
+
+ if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = unit_dbus_path(UNIT(s))))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+ char *introspection = NULL;
+ FILE *f;
+ Iterator i;
+ Unit *u;
+ Job *j;
+ const char *k;
+ size_t size;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ /* We roll our own introspection code here, instead of
+ * relying on bus_default_message_handler() because we
+ * need to generate our introspection string
+ * dynamically. */
+
+ if (!(f = open_memstream(&introspection, &size)))
+ goto oom;
+
+ fputs(INTROSPECTION_BEGIN, f);
+
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+ char *p;
+
+ if (k != u->id)
+ continue;
+
+ if (!(p = bus_path_escape(k))) {
+ fclose(f);
+ free(introspection);
+ goto oom;
+ }
+
+ fprintf(f, "<node name=\"unit/%s\"/>", p);
+ free(p);
+ }
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
+
+ fputs(INTROSPECTION_END, f);
+
+ if (ferror(f)) {
+ fclose(f);
+ free(introspection);
+ goto oom;
+ }
+
+ fclose(f);
+
+ if (!introspection)
+ goto oom;
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
+ free(introspection);
+ goto oom;
+ }
+
+ free(introspection);
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
+
+ assert(!m->queued_message);
+
+ /* Instead of sending the reply back right away, we
+ * just remember that we need to and then send it
+ * after the reload is finished. That way the caller
+ * knows when the reload finished. */
+
+ if (!(m->queued_message = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->queued_message_connection = connection;
+ m->exit_code = MANAGER_RELOAD;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
+
+ /* We don't send a reply back here, the client should
+ * just wait for us disconnecting. */
+
+ m->exit_code = MANAGER_REEXECUTE;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
+
+ if (m->running_as == MANAGER_SYSTEM) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
+ return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->exit_code = MANAGER_EXIT;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
+
+ if (m->running_as != MANAGER_SYSTEM) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
+ return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->exit_code = MANAGER_REBOOT;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
+
+ if (m->running_as != MANAGER_SYSTEM) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
+ return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->exit_code = MANAGER_POWEROFF;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
+
+ if (m->running_as != MANAGER_SYSTEM) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
+ return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->exit_code = MANAGER_HALT;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
+
+ if (m->running_as != MANAGER_SYSTEM) {
+ dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
+ return bus_send_error_reply(connection, message, &error, -ENOTSUP);
+ }
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ m->exit_code = MANAGER_KEXEC;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
+ char **l = NULL, **e = NULL;
+
+ if ((r = bus_parse_strv(message, &l)) < 0) {
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ e = strv_env_merge(2, m->environment, l);
+ strv_free(l);
+
+ if (!e)
+ goto oom;
+
+ if (!(reply = dbus_message_new_method_return(message))) {
+ strv_free(e);
+ goto oom;
+ }
+
+ strv_free(m->environment);
+ m->environment = e;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
+ char **l = NULL, **e = NULL;
+
+ if ((r = bus_parse_strv(message, &l)) < 0) {
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ e = strv_env_delete(m->environment, 1, l);
+ strv_free(l);
+
+ if (!e)
+ goto oom;
+
+ if (!(reply = dbus_message_new_method_return(message))) {
+ strv_free(e);
+ goto oom;
+ }
+
+ strv_free(m->environment);
+ m->environment = e;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
+ char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_parse_strv_iter(&iter, &l_unset);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ if (!dbus_message_iter_next(&iter)) {
+ strv_free(l_unset);
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
+ }
+
+ r = bus_parse_strv_iter(&iter, &l_set);
+ if (r < 0) {
+ strv_free(l_unset);
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ e = strv_env_delete(m->environment, 1, l_unset);
+ strv_free(l_unset);
+
+ if (!e) {
+ strv_free(l_set);
+ goto oom;
+ }
+
+ f = strv_env_merge(2, e, l_set);
+ strv_free(l_set);
+ strv_free(e);
+
+ if (!f)
+ goto oom;
+
+ if (!(reply = dbus_message_new_method_return(message))) {
+ strv_free(f);
+ goto oom;
+ }
+
+ strv_free(m->environment);
+ m->environment = f;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
+ DBusMessageIter iter, sub, sub2;
+ Hashmap *h;
+ Iterator i;
+ UnitFileList *item;
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h)
+ goto oom;
+
+ r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
+ if (r < 0) {
+ unit_file_list_free(h);
+ dbus_message_unref(reply);
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
+ unit_file_list_free(h);
+ goto oom;
+ }
+
+ HASHMAP_FOREACH(item, h, i) {
+ const char *state;
+
+ state = unit_file_state_to_string(item->state);
+ assert(state);
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
+ !dbus_message_iter_close_container(&sub, &sub2)) {
+ unit_file_list_free(h);
+ goto oom;
+ }
+ }
+
+ unit_file_list_free(h);
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
+ const char *name;
+ UnitFileState state;
+ const char *s;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ state = unit_file_get_state(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
+ if (state < 0)
+ return bus_send_error_reply(connection, message, NULL, state);
+
+ s = unit_file_state_to_string(state);
+ assert(s);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
+
+ char **l = NULL;
+ DBusMessageIter iter;
+ UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ dbus_bool_t runtime, force;
+ int carries_install_info = -1;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_parse_strv_iter(&iter, &l);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ if (!dbus_message_iter_next(&iter) ||
+ bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
+ bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
+ strv_free(l);
+ return bus_send_error_reply(connection, message, NULL, -EIO);
+ }
+
+ if (streq(member, "EnableUnitFiles")) {
+ r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(member, "ReenableUnitFiles")) {
+ r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(member, "LinkUnitFiles"))
+ r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
+ else if (streq(member, "PresetUnitFiles")) {
+ r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(member, "MaskUnitFiles"))
+ r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
+ else
+ assert_not_reached("Uh? Wrong method");
+
+ strv_free(l);
+ bus_manager_send_unit_files_changed(m);
+
+ if (r < 0) {
+ unit_file_changes_free(changes, n_changes);
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
+ unit_file_changes_free(changes, n_changes);
+
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
+
+ char **l = NULL;
+ DBusMessageIter iter;
+ UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ dbus_bool_t runtime;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_parse_strv_iter(&iter, &l);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ goto oom;
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ if (!dbus_message_iter_next(&iter) ||
+ bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
+ strv_free(l);
+ return bus_send_error_reply(connection, message, NULL, -EIO);
+ }
+
+ if (streq(member, "DisableUnitFiles"))
+ r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
+ else if (streq(member, "UnmaskUnitFiles"))
+ r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
+ else
+ assert_not_reached("Uh? Wrong method");
+
+ strv_free(l);
+ bus_manager_send_unit_files_changed(m);
+
+ if (r < 0) {
+ unit_file_changes_free(changes, n_changes);
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ reply = message_from_file_changes(message, changes, n_changes, -1);
+ unit_file_changes_free(changes, n_changes);
+
+ if (!reply)
+ goto oom;
+
+ } else {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
+ { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
+ { NULL, }
+ };
+ return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
+ }
+
+ if (job_type != _JOB_TYPE_INVALID) {
+ const char *name, *smode, *old_name = NULL;
+ JobMode mode;
+ Job *j;
+ Unit *u;
+ bool b;
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
+ b = dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &old_name,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &smode,
+ DBUS_TYPE_INVALID);
+ else
+ b = dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &smode,
+ DBUS_TYPE_INVALID);
+
+ if (!b)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (old_name)
+ if (!(u = manager_get_unit(m, old_name)) ||
+ !u->job ||
+ u->job->type != JOB_START) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+
+ if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
+ dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (reload_if_possible && unit_can_reload(u)) {
+ if (job_type == JOB_RESTART)
+ job_type = JOB_RELOAD_OR_START;
+ else if (job_type == JOB_TRY_RESTART)
+ job_type = JOB_RELOAD;
+ }
+
+ if ((job_type == JOB_START && u->refuse_manual_start) ||
+ (job_type == JOB_STOP && u->refuse_manual_stop) ||
+ ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
+ (u->refuse_manual_start || u->refuse_manual_stop))) {
+ dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
+ return bus_send_error_reply(connection, message, &error, -EPERM);
+ }
+
+ if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(j->bus_client = strdup(message_get_sender_with_fallback(message))))
+ goto oom;
+
+ j->bus = connection;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = job_dbus_path(j)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (reply) {
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+ }
+
+ free(path);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ free(path);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+const DBusObjectPathVTable bus_manager_vtable = {
+ .message_function = bus_manager_message_handler
+};
diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h
new file mode 100644
index 0000000..2eb2fbb
--- /dev/null
+++ b/src/core/dbus-manager.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusmanagerhfoo
+#define foodbusmanagerhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+extern const DBusObjectPathVTable bus_manager_vtable;
+
+extern const char bus_manager_interface[];
+
+#endif
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
new file mode 100644
index 0000000..35d6ea7
--- /dev/null
+++ b/src/core/dbus-mount.c
@@ -0,0 +1,168 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-mount.h"
+#include "dbus-execute.h"
+#include "dbus-common.h"
+
+#define BUS_MOUNT_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Mount\">\n" \
+ " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Options\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_EXEC_COMMAND_INTERFACE("ExecMount") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecUnmount") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \
+ BUS_EXEC_CONTEXT_INTERFACE \
+ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_MOUNT_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Mount\0"
+
+const char bus_mount_interface[] _introspect_("Mount") = BUS_MOUNT_INTERFACE;
+
+const char bus_mount_invalidating_properties[] =
+ "What\0"
+ "Options\0"
+ "Type\0"
+ "ExecMount\0"
+ "ExecUnmount\0"
+ "ExecRemount\0"
+ "ControlPID\0"
+ "Result\0";
+
+static int bus_mount_append_what(DBusMessageIter *i, const char *property, void *data) {
+ Mount *m = data;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what)
+ d = m->parameters_proc_self_mountinfo.what;
+ else if (m->from_fragment && m->parameters_fragment.what)
+ d = m->parameters_fragment.what;
+ else if (m->from_etc_fstab && m->parameters_etc_fstab.what)
+ d = m->parameters_etc_fstab.what;
+ else
+ d = "";
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_mount_append_options(DBusMessageIter *i, const char *property, void *data) {
+ Mount *m = data;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options)
+ d = m->parameters_proc_self_mountinfo.options;
+ else if (m->from_fragment && m->parameters_fragment.options)
+ d = m->parameters_fragment.options;
+ else if (m->from_etc_fstab && m->parameters_etc_fstab.options)
+ d = m->parameters_etc_fstab.options;
+ else
+ d = "";
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_mount_append_type(DBusMessageIter *i, const char *property, void *data) {
+ Mount *m = data;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype)
+ d = m->parameters_proc_self_mountinfo.fstype;
+ else if (m->from_fragment && m->parameters_fragment.fstype)
+ d = m->parameters_fragment.fstype;
+ else if (m->from_etc_fstab && m->parameters_etc_fstab.fstype)
+ d = m->parameters_etc_fstab.fstype;
+ else
+ d = "";
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_mount_append_mount_result, mount_result, MountResult);
+
+static const BusProperty bus_mount_properties[] = {
+ { "Where", bus_property_append_string, "s", offsetof(Mount, where), true },
+ { "What", bus_mount_append_what, "s", 0 },
+ { "Options", bus_mount_append_options, "s", 0 },
+ { "Type", bus_mount_append_type, "s", 0 },
+ { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Mount, timeout_usec) },
+ BUS_EXEC_COMMAND_PROPERTY("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), false),
+ BUS_EXEC_COMMAND_PROPERTY("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), false),
+ BUS_EXEC_COMMAND_PROPERTY("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), false),
+ { "ControlPID", bus_property_append_pid, "u", offsetof(Mount, control_pid) },
+ { "DirectoryMode", bus_property_append_mode, "u", offsetof(Mount, directory_mode) },
+ { "Result", bus_mount_append_mount_result, "s", offsetof(Mount, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Mount *m = MOUNT(u);
+
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
+ { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps );
+}
diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h
new file mode 100644
index 0000000..b5613fa
--- /dev/null
+++ b/src/core/dbus-mount.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusmounthfoo
+#define foodbusmounthfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_mount_interface[];
+extern const char bus_mount_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
new file mode 100644
index 0000000..5506784
--- /dev/null
+++ b/src/core/dbus-path.c
@@ -0,0 +1,119 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-path.h"
+#include "dbus-execute.h"
+#include "dbus-common.h"
+
+#define BUS_PATH_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Path\">\n" \
+ " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Paths\" type=\"a(ss)\" access=\"read\"/>\n" \
+ " <property name=\"MakeDirectory\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_PATH_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Path\0"
+
+const char bus_path_interface[] _introspect_("Path") = BUS_PATH_INTERFACE;
+
+const char bus_path_invalidating_properties[] =
+ "Result\0";
+
+static int bus_path_append_paths(DBusMessageIter *i, const char *property, void *data) {
+ Path *p = data;
+ DBusMessageIter sub, sub2;
+ PathSpec *k;
+
+ assert(i);
+ assert(property);
+ assert(p);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(spec, k, p->specs) {
+ const char *t = path_type_to_string(k->type);
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &k->path) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ Path *p = PATH(u);
+ const char *t;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : "";
+
+ return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_path_append_path_result, path_result, PathResult);
+
+static const BusProperty bus_path_properties[] = {
+ { "Unit", bus_path_append_unit, "s", 0 },
+ { "Paths", bus_path_append_paths, "a(ss)", 0 },
+ { "MakeDirectory", bus_property_append_bool, "b", offsetof(Path, make_directory) },
+ { "DirectoryMode", bus_property_append_mode, "u", offsetof(Path, directory_mode) },
+ { "Result", bus_path_append_path_result, "s", offsetof(Path, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Path *p = PATH(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Path", bus_path_properties, p },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h
new file mode 100644
index 0000000..2888400
--- /dev/null
+++ b/src/core/dbus-path.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbuspathhfoo
+#define foodbuspathhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_path_interface[];
+
+extern const char bus_path_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
new file mode 100644
index 0000000..d840415
--- /dev/null
+++ b/src/core/dbus-service.c
@@ -0,0 +1,169 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-execute.h"
+#include "dbus-service.h"
+#include "dbus-common.h"
+
+#ifdef HAVE_SYSV_COMPAT
+#define BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \
+ " <property name=\"SysVStartPriority\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"SysVRunLevels\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"SysVPath\" type=\"s\" access=\"read\"/>\n"
+#else
+#define BUS_SERVICE_SYSV_INTERFACE_FRAGMENT ""
+#endif
+
+#define BUS_SERVICE_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Service\">\n" \
+ " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Restart\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"PIDFile\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"StartLimitAction\" type=\"s\" access=\"readwrite\"/>\n" \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStart") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecReload") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStop") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
+ BUS_EXEC_CONTEXT_INTERFACE \
+ " <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
+ BUS_EXEC_STATUS_INTERFACE("ExecMain") \
+ " <property name=\"MainPID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"FsckPassNo\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SERVICE_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Service\0"
+
+const char bus_service_interface[] _introspect_("Service") = BUS_SERVICE_INTERFACE;
+
+const char bus_service_invalidating_properties[] =
+ "ExecStartPre\0"
+ "ExecStart\0"
+ "ExecStartPost\0"
+ "ExecReload\0"
+ "ExecStop\0"
+ "ExecStopPost\0"
+ "ExecMain\0"
+ "WatchdogTimestamp\0"
+ "WatchdogTimestampMonotonic\0"
+ "MainPID\0"
+ "ControlPID\0"
+ "StatusText\0"
+ "Result\0";
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction);
+static DEFINE_BUS_PROPERTY_SET_ENUM(bus_service_set_start_limit_action, start_limit_action, StartLimitAction);
+
+static const BusProperty bus_exec_main_status_properties[] = {
+ { "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
+ { "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
+ { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
+ { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
+ { "ExecMainPID", bus_property_append_pid, "u", offsetof(ExecStatus, pid) },
+ { "ExecMainCode", bus_property_append_int, "i", offsetof(ExecStatus, code) },
+ { "ExecMainStatus", bus_property_append_int, "i", offsetof(ExecStatus, status) },
+ { NULL, }
+};
+
+static const BusProperty bus_service_properties[] = {
+ { "Type", bus_service_append_type, "s", offsetof(Service, type) },
+ { "Restart", bus_service_append_restart, "s", offsetof(Service, restart) },
+ { "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true },
+ { "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) },
+ { "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) },
+ { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_usec) },
+ { "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) },
+ { "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) },
+ { "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) },
+ { "StartLimitInterval", bus_property_append_usec, "t", offsetof(Service, start_limit.interval) },
+ { "StartLimitBurst", bus_property_append_uint32, "u", offsetof(Service, start_limit.burst) },
+ { "StartLimitAction", bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action), false, bus_service_set_start_limit_action},
+ BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), true ),
+ { "PermissionsStartOnly", bus_property_append_bool, "b", offsetof(Service, permissions_start_only) },
+ { "RootDirectoryStartOnly", bus_property_append_bool, "b", offsetof(Service, root_directory_start_only) },
+ { "RemainAfterExit", bus_property_append_bool, "b", offsetof(Service, remain_after_exit) },
+ { "GuessMainPID", bus_property_append_bool, "b", offsetof(Service, guess_main_pid) },
+ { "MainPID", bus_property_append_pid, "u", offsetof(Service, main_pid) },
+ { "ControlPID", bus_property_append_pid, "u", offsetof(Service, control_pid) },
+ { "BusName", bus_property_append_string, "s", offsetof(Service, bus_name), true },
+ { "StatusText", bus_property_append_string, "s", offsetof(Service, status_text), true },
+#ifdef HAVE_SYSV_COMPAT
+ { "SysVRunLevels", bus_property_append_string, "s", offsetof(Service, sysv_runlevels), true },
+ { "SysVStartPriority", bus_property_append_int, "i", offsetof(Service, sysv_start_priority) },
+ { "SysVPath", bus_property_append_string, "s", offsetof(Service, sysv_path), true },
+#endif
+ { "FsckPassNo", bus_property_append_int, "i", offsetof(Service, fsck_passno) },
+ { "Result", bus_service_append_service_result,"s", offsetof(Service, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) {
+ Service *s = SERVICE(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Service", bus_service_properties, s },
+ { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context },
+ { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h
new file mode 100644
index 0000000..d6eab65
--- /dev/null
+++ b/src/core/dbus-service.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusservicehfoo
+#define foodbusservicehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_service_interface[];
+extern const char bus_service_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
new file mode 100644
index 0000000..e69388a
--- /dev/null
+++ b/src/core/dbus-snapshot.c
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "dbus-unit.h"
+#include "dbus-snapshot.h"
+#include "dbus-common.h"
+
+#define BUS_SNAPSHOT_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Snapshot\">\n" \
+ " <method name=\"Remove\"/>\n" \
+ " <property name=\"Cleanup\" type=\"b\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SNAPSHOT_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Snapshot\0"
+
+const char bus_snapshot_interface[] _introspect_("Snapshot") = BUS_SNAPSHOT_INTERFACE;
+
+static const BusProperty bus_snapshot_properties[] = {
+ { "Cleanup", bus_property_append_bool, "b", offsetof(Snapshot, cleanup) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Snapshot *s = SNAPSHOT(u);
+
+ DBusMessage *reply = NULL;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) {
+
+ snapshot_remove(SNAPSHOT(u));
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Snapshot", bus_snapshot_properties, s },
+ { NULL, }
+ };
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+ }
+
+ if (reply) {
+ if (!dbus_connection_send(c, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
diff --git a/src/core/dbus-snapshot.h b/src/core/dbus-snapshot.h
new file mode 100644
index 0000000..0b82279
--- /dev/null
+++ b/src/core/dbus-snapshot.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbussnapshothfoo
+#define foodbussnapshothfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_snapshot_interface[];
+
+#endif
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
new file mode 100644
index 0000000..2e3342c
--- /dev/null
+++ b/src/core/dbus-socket.c
@@ -0,0 +1,139 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-socket.h"
+#include "dbus-execute.h"
+#include "dbus-common.h"
+
+#define BUS_SOCKET_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Socket\">\n" \
+ " <property name=\"BindIPv6Only\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Backlog\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStopPre") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
+ BUS_EXEC_CONTEXT_INTERFACE \
+ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"SocketMode\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Accept\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"KeepAlive\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"ReceiveBuffer\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"SendBuffer\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"IPTOS\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"IPTTL\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"PipeSize\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"FreeBind\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Transparent\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Broadcast\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"PassCredentials\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"PassSecurity\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Mark\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"MaxConnections\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"NAccepted\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \
+ " <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n" \
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SOCKET_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Socket\0"
+
+const char bus_socket_interface[] _introspect_("Socket") = BUS_SOCKET_INTERFACE;
+
+const char bus_socket_invalidating_properties[] =
+ "ExecStartPre\0"
+ "ExecStartPost\0"
+ "ExecStopPre\0"
+ "ExecStopPost\0"
+ "ControlPID\0"
+ "NAccepted\0"
+ "NConnections\0"
+ "Result\0";
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult);
+
+static const BusProperty bus_socket_properties[] = {
+ { "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) },
+ { "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) },
+ { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Socket, timeout_usec) },
+ BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), true ),
+ BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), true ),
+ { "ControlPID", bus_property_append_pid, "u", offsetof(Socket, control_pid) },
+ { "BindToDevice", bus_property_append_string, "s", offsetof(Socket, bind_to_device), true },
+ { "DirectoryMode", bus_property_append_mode, "u", offsetof(Socket, directory_mode) },
+ { "SocketMode", bus_property_append_mode, "u", offsetof(Socket, socket_mode) },
+ { "Accept", bus_property_append_bool, "b", offsetof(Socket, accept) },
+ { "KeepAlive", bus_property_append_bool, "b", offsetof(Socket, keep_alive) },
+ { "Priority", bus_property_append_int, "i", offsetof(Socket, priority) },
+ { "ReceiveBuffer", bus_property_append_size, "t", offsetof(Socket, receive_buffer) },
+ { "SendBuffer", bus_property_append_size, "t", offsetof(Socket, send_buffer) },
+ { "IPTOS", bus_property_append_int, "i", offsetof(Socket, ip_tos) },
+ { "IPTTL", bus_property_append_int, "i", offsetof(Socket, ip_ttl) },
+ { "PipeSize", bus_property_append_size, "t", offsetof(Socket, pipe_size) },
+ { "FreeBind", bus_property_append_bool, "b", offsetof(Socket, free_bind) },
+ { "Transparent", bus_property_append_bool, "b", offsetof(Socket, transparent) },
+ { "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) },
+ { "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) },
+ { "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) },
+ { "Mark", bus_property_append_int, "i", offsetof(Socket, mark) },
+ { "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) },
+ { "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) },
+ { "NAccepted", bus_property_append_unsigned, "u", offsetof(Socket, n_accepted) },
+ { "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) },
+ { "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) },
+ { "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Socket *s = SOCKET(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
+ { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h
new file mode 100644
index 0000000..069a2f5
--- /dev/null
+++ b/src/core/dbus-socket.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbussockethfoo
+#define foodbussockethfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_socket_interface[];
+extern const char bus_socket_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
new file mode 100644
index 0000000..09cd1e8
--- /dev/null
+++ b/src/core/dbus-swap.c
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Maarten Lankhorst
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-swap.h"
+#include "dbus-execute.h"
+#include "dbus-common.h"
+
+#define BUS_SWAP_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Swap\">\n" \
+ " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_EXEC_COMMAND_INTERFACE("ExecActivate") \
+ BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \
+ BUS_EXEC_CONTEXT_INTERFACE \
+ " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SWAP_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Swap\0"
+
+const char bus_swap_interface[] _introspect_("Swap") = BUS_SWAP_INTERFACE;
+
+const char bus_swap_invalidating_properties[] =
+ "What\0"
+ "Priority\0"
+ "ExecActivate\0"
+ "ExecDeactivate\0"
+ "ControlPID\0"
+ "Result\0";
+
+static int bus_swap_append_priority(DBusMessageIter *i, const char *property, void *data) {
+ Swap *s = data;
+ dbus_int32_t j;
+
+ assert(i);
+ assert(property);
+ assert(s);
+
+ if (s->from_proc_swaps)
+ j = s->parameters_proc_swaps.priority;
+ else if (s->from_fragment)
+ j = s->parameters_fragment.priority;
+ else if (s->from_etc_fstab)
+ j = s->parameters_etc_fstab.priority;
+ else
+ j = -1;
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &j))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_swap_append_swap_result, swap_result, SwapResult);
+
+static const BusProperty bus_swap_properties[] = {
+ { "What", bus_property_append_string, "s", offsetof(Swap, what), true },
+ { "Priority", bus_swap_append_priority, "i", 0 },
+ BUS_EXEC_COMMAND_PROPERTY("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), false),
+ BUS_EXEC_COMMAND_PROPERTY("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), false),
+ { "ControlPID", bus_property_append_pid, "u", offsetof(Swap, control_pid) },
+ { "Result", bus_swap_append_swap_result,"s", offsetof(Swap, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Swap *s = SWAP(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
+ { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h
new file mode 100644
index 0000000..15b9147
--- /dev/null
+++ b/src/core/dbus-swap.h
@@ -0,0 +1,35 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusswaphfoo
+#define foodbusswaphfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Maarten Lankhorst
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_swap_interface[];
+extern const char bus_swap_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c
new file mode 100644
index 0000000..55cf862
--- /dev/null
+++ b/src/core/dbus-target.c
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-target.h"
+#include "dbus-common.h"
+
+#define BUS_TARGET_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Target\">\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_TARGET_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Target\0"
+
+const char bus_target_interface[] _introspect_("Target") = BUS_TARGET_INTERFACE;
+
+DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h
new file mode 100644
index 0000000..13d3876
--- /dev/null
+++ b/src/core/dbus-target.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbustargethfoo
+#define foodbustargethfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_target_interface[];
+
+#endif
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
new file mode 100644
index 0000000..b396aed
--- /dev/null
+++ b/src/core/dbus-timer.c
@@ -0,0 +1,137 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-timer.h"
+#include "dbus-execute.h"
+#include "dbus-common.h"
+
+#define BUS_TIMER_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Timer\">\n" \
+ " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \
+ " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_TIMER_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Timer\0"
+
+const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE;
+
+const char bus_timer_invalidating_properties[] =
+ "Timers\0"
+ "NextElapseUSec\0"
+ "Result\0";
+
+static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) {
+ Timer *p = data;
+ DBusMessageIter sub, sub2;
+ TimerValue *k;
+
+ assert(i);
+ assert(property);
+ assert(p);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(stt)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(value, k, p->values) {
+ char *buf;
+ const char *t;
+ size_t l;
+ bool b;
+
+ t = timer_base_to_string(k->base);
+ assert(endswith(t, "Sec"));
+
+ /* s/Sec/USec/ */
+ l = strlen(t);
+ if (!(buf = new(char, l+2)))
+ return -ENOMEM;
+
+ memcpy(buf, t, l-3);
+ memcpy(buf+l-3, "USec", 5);
+
+ b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->value) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
+ dbus_message_iter_close_container(&sub, &sub2);
+
+ free(buf);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ Timer *timer = TIMER(u);
+ const char *t;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : "";
+
+ return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult);
+
+static const BusProperty bus_timer_properties[] = {
+ { "Unit", bus_timer_append_unit, "s", 0 },
+ { "Timers", bus_timer_append_timers, "a(stt)", 0 },
+ { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse) },
+ { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) },
+ { NULL, }
+};
+
+DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Timer *t = TIMER(u);
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Timer", bus_timer_properties, t },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h
new file mode 100644
index 0000000..e692e12
--- /dev/null
+++ b/src/core/dbus-timer.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbustimerhfoo
+#define foodbustimerhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+extern const char bus_timer_interface[];
+extern const char bus_timer_invalidating_properties[];
+
+#endif
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
new file mode 100644
index 0000000..c7532c7
--- /dev/null
+++ b/src/core/dbus-unit.c
@@ -0,0 +1,850 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "dbus-unit.h"
+#include "bus-errors.h"
+#include "dbus-common.h"
+
+const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
+
+#define INVALIDATING_PROPERTIES \
+ "LoadState\0" \
+ "ActiveState\0" \
+ "SubState\0" \
+ "InactiveExitTimestamp\0" \
+ "ActiveEnterTimestamp\0" \
+ "ActiveExitTimestamp\0" \
+ "InactiveEnterTimestamp\0" \
+ "Job\0" \
+ "NeedDaemonReload\0"
+
+static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
+ char *t;
+ Iterator j;
+ DBusMessageIter sub;
+ Unit *u = data;
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
+ return -ENOMEM;
+
+ SET_FOREACH(t, u->names, j)
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data, *f;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ f = unit_following(u);
+ d = f ? f->id : "";
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u;
+ Iterator j;
+ DBusMessageIter sub;
+ Set *s = data;
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
+ return -ENOMEM;
+
+ SET_FOREACH(u, s, j)
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ d = unit_description(u);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
+
+static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *state;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ state = unit_active_state_to_string(unit_active_state(u));
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *state;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ state = unit_sub_state_to_string(u);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *state;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ b = unit_can_start(u) &&
+ !u->refuse_manual_start;
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ /* On the lower levels we assume that every unit we can start
+ * we can also stop */
+
+ b = unit_can_start(u) &&
+ !u->refuse_manual_stop;
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ b = unit_can_reload(u);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ b = unit_can_isolate(u) &&
+ !u->refuse_manual_start;
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ DBusMessageIter sub;
+ char *p;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
+ return -ENOMEM;
+
+ if (u->job) {
+
+ if (!(p = job_dbus_path(u->job)))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
+ free(p);
+ return -ENOMEM;
+ }
+ } else {
+ uint32_t id = 0;
+
+ /* No job, so let's fill in some placeholder
+ * data. Since we need to fill in a valid path we
+ * simple point to ourselves. */
+
+ if (!(p = unit_dbus_path(u)))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
+ free(p);
+ return -ENOMEM;
+ }
+ }
+
+ free(p);
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ char *t;
+ CGroupBonding *cgb;
+ bool success;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ if ((cgb = unit_get_default_cgroup(u))) {
+ if (!(t = cgroup_bonding_to_string(cgb)))
+ return -ENOMEM;
+ } else
+ t = (char*) "";
+
+ success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
+
+ if (cgb)
+ free(t);
+
+ return success ? 0 : -ENOMEM;
+}
+
+static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ CGroupBonding *cgb;
+ DBusMessageIter sub;
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
+ char *t;
+ bool success;
+
+ if (!(t = cgroup_bonding_to_string(cgb)))
+ return -ENOMEM;
+
+ success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
+ free(t);
+
+ if (!success)
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ CGroupAttribute *a;
+ DBusMessageIter sub, sub2;
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ char *v = NULL;
+ bool success;
+
+ if (a->map_callback)
+ a->map_callback(a->controller, a->name, a->value, &v);
+
+ success =
+ dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
+ dbus_message_iter_close_container(&sub, &sub2);
+
+ free(v);
+
+ if (!success)
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ dbus_bool_t b;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ b = unit_need_daemon_reload(u);
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *name, *message;
+ DBusMessageIter sub;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ if (u->load_error != 0) {
+ name = bus_errno_to_dbus(u->load_error);
+ message = strempty(strerror(-u->load_error));
+ } else
+ name = message = "";
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
+ !dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
+ DBusMessage *reply = NULL;
+ Manager *m = u->manager;
+ DBusError error;
+ JobType job_type = _JOB_TYPE_INVALID;
+ char *path = NULL;
+ bool reload_if_possible = false;
+
+ dbus_error_init(&error);
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
+ job_type = JOB_START;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
+ job_type = JOB_STOP;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
+ job_type = JOB_RELOAD;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
+ job_type = JOB_RESTART;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
+ job_type = JOB_TRY_RESTART;
+ else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
+ reload_if_possible = true;
+ job_type = JOB_RESTART;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
+ reload_if_possible = true;
+ job_type = JOB_TRY_RESTART;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
+ const char *swho, *smode;
+ int32_t signo;
+ KillMode mode;
+ KillWho who;
+ int r;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &swho,
+ DBUS_TYPE_STRING, &smode,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (isempty(swho))
+ who = KILL_ALL;
+ else {
+ who = kill_who_from_string(swho);
+ if (who < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (isempty(smode))
+ mode = KILL_CONTROL_GROUP;
+ else {
+ mode = kill_mode_from_string(smode);
+ if (mode < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
+
+ unit_reset_failed(u);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ } else if (UNIT_VTABLE(u)->bus_message_handler)
+ return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (job_type != _JOB_TYPE_INVALID) {
+ const char *smode;
+ JobMode mode;
+ Job *j;
+ int r;
+
+ if ((job_type == JOB_START && u->refuse_manual_start) ||
+ (job_type == JOB_STOP && u->refuse_manual_stop) ||
+ ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
+ (u->refuse_manual_start || u->refuse_manual_stop))) {
+ dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
+ return bus_send_error_reply(connection, message, &error, -EPERM);
+ }
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &smode,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (reload_if_possible && unit_can_reload(u)) {
+ if (job_type == JOB_RESTART)
+ job_type = JOB_RELOAD_OR_START;
+ else if (job_type == JOB_TRY_RESTART)
+ job_type = JOB_RELOAD;
+ }
+
+ if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
+ dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ if (!(path = job_dbus_path(j)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (reply) {
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+ }
+
+ free(path);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ free(path);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ Unit *u;
+ int r;
+ DBusMessage *reply;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
+ /* Be nice to gdbus and return introspection data for our mid-level paths */
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+ char *introspection = NULL;
+ FILE *f;
+ Iterator i;
+ const char *k;
+ size_t size;
+
+ if (!(reply = dbus_message_new_method_return(message)))
+ goto oom;
+
+ /* We roll our own introspection code here, instead of
+ * relying on bus_default_message_handler() because we
+ * need to generate our introspection string
+ * dynamically. */
+
+ if (!(f = open_memstream(&introspection, &size)))
+ goto oom;
+
+ fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node>\n", f);
+
+ fputs(BUS_INTROSPECTABLE_INTERFACE, f);
+ fputs(BUS_PEER_INTERFACE, f);
+
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+ char *p;
+
+ if (k != u->id)
+ continue;
+
+ if (!(p = bus_path_escape(k))) {
+ fclose(f);
+ free(introspection);
+ goto oom;
+ }
+
+ fprintf(f, "<node name=\"%s\"/>", p);
+ free(p);
+ }
+
+ fputs("</node>\n", f);
+
+ if (ferror(f)) {
+ fclose(f);
+ free(introspection);
+ goto oom;
+ }
+
+ fclose(f);
+
+ if (!introspection)
+ goto oom;
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
+ free(introspection);
+ goto oom;
+ }
+
+ free(introspection);
+
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
+
+ if (r == -ENOMEM)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (r == -ENOENT) {
+ DBusError e;
+
+ dbus_error_init(&e);
+ dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
+ return bus_send_error_reply(connection, message, &e, r);
+ }
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ return bus_unit_message_dispatch(u, connection, message);
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+const DBusObjectPathVTable bus_unit_vtable = {
+ .message_function = bus_unit_message_handler
+};
+
+void bus_unit_send_change_signal(Unit *u) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(u);
+
+ if (u->in_dbus_queue) {
+ LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
+ u->in_dbus_queue = false;
+ }
+
+ if (!u->id)
+ return;
+
+ if (!bus_has_subscriber(u->manager)) {
+ u->sent_dbus_new_signal = true;
+ return;
+ }
+
+ if (!(p = unit_dbus_path(u)))
+ goto oom;
+
+ if (u->sent_dbus_new_signal) {
+ /* Send a properties changed signal. First for the
+ * specific type, then for the generic unit. The
+ * clients may rely on this order to get atomic
+ * behaviour if needed. */
+
+ if (UNIT_VTABLE(u)->bus_invalidating_properties) {
+
+ if (!(m = bus_properties_changed_new(p,
+ UNIT_VTABLE(u)->bus_interface,
+ UNIT_VTABLE(u)->bus_invalidating_properties)))
+ goto oom;
+
+ if (bus_broadcast(u->manager, m) < 0)
+ goto oom;
+
+ dbus_message_unref(m);
+ }
+
+ if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
+ goto oom;
+
+ } else {
+ /* Send a new signal */
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
+ goto oom;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &u->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ if (bus_broadcast(u->manager, m) < 0)
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ u->sent_dbus_new_signal = true;
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate unit change/new signal.");
+}
+
+void bus_unit_send_removed_signal(Unit *u) {
+ char *p = NULL;
+ DBusMessage *m = NULL;
+
+ assert(u);
+
+ if (!bus_has_subscriber(u->manager))
+ return;
+
+ if (!u->sent_dbus_new_signal)
+ bus_unit_send_change_signal(u);
+
+ if (!u->id)
+ return;
+
+ if (!(p = unit_dbus_path(u)))
+ goto oom;
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
+ goto oom;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &u->id,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (bus_broadcast(u->manager, m) < 0)
+ goto oom;
+
+ free(p);
+ dbus_message_unref(m);
+
+ return;
+
+oom:
+ free(p);
+
+ if (m)
+ dbus_message_unref(m);
+
+ log_error("Failed to allocate unit remove signal.");
+}
+
+const BusProperty bus_unit_properties[] = {
+ { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
+ { "Names", bus_unit_append_names, "as", 0 },
+ { "Following", bus_unit_append_following, "s", 0 },
+ { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
+ { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
+ { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
+ { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
+ { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
+ { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true },
+ { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
+ { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
+ { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
+ { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
+ { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
+ { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
+ { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
+ { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
+ { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
+ { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
+ { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
+ { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true },
+ { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true },
+ { "Description", bus_unit_append_description, "s", 0 },
+ { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
+ { "ActiveState", bus_unit_append_active_state, "s", 0 },
+ { "SubState", bus_unit_append_sub_state, "s", 0 },
+ { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
+ { "UnitFileState", bus_unit_append_file_state, "s", 0 },
+ { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
+ { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
+ { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
+ { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
+ { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
+ { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
+ { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
+ { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
+ { "CanStart", bus_unit_append_can_start, "b", 0 },
+ { "CanStop", bus_unit_append_can_stop, "b", 0 },
+ { "CanReload", bus_unit_append_can_reload, "b", 0 },
+ { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
+ { "Job", bus_unit_append_job, "(uo)", 0 },
+ { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
+ { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
+ { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
+ { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
+ { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
+ { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
+ { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
+ { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
+ { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
+ { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
+ { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
+ { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
+ { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
+ { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
+ { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
+ { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
+ { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
+ { NULL, }
+};
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
new file mode 100644
index 0000000..4f19a80
--- /dev/null
+++ b/src/core/dbus-unit.h
@@ -0,0 +1,139 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbusunithfoo
+#define foodbusunithfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "manager.h"
+#include "dbus-common.h"
+
+#define BUS_UNIT_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Unit\">\n" \
+ " <method name=\"Start\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Stop\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Reload\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Restart\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"TryRestart\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReloadOrRestart\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ReloadOrTryRestart\">\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"Kill\">\n" \
+ " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"ResetFailed\"/>\n" \
+ " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Names\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Requires\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"RequiresOverridable\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"BindTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"After\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"OnFailure\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Triggers\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"TriggeredBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"PropagateReloadTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"PropagateReloadFrom\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"SubState\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"UnitFileState\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"InactiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ActiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ActiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"InactiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"InactiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"CanStart\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"CanStop\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"CanReload\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"CanIsolate\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Job\" type=\"(uo)\" access=\"read\"/>\n" \
+ " <property name=\"StopWhenUnneeded\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"RefuseManualStart\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"RefuseManualStop\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"AllowIsolate\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"DefaultDependencies\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
+ " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define BUS_UNIT_INTERFACES_LIST \
+ BUS_GENERIC_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Unit\0"
+
+extern const BusProperty bus_unit_properties[];
+
+void bus_unit_send_change_signal(Unit *u);
+void bus_unit_send_removed_signal(Unit *u);
+
+extern const DBusObjectPathVTable bus_unit_vtable;
+
+extern const char bus_unit_interface[];
+
+#endif
diff --git a/src/core/dbus.c b/src/core/dbus.c
new file mode 100644
index 0000000..ddf91f2
--- /dev/null
+++ b/src/core/dbus.c
@@ -0,0 +1,1483 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dbus/dbus.h>
+
+#include "dbus.h"
+#include "log.h"
+#include "strv.h"
+#include "cgroup.h"
+#include "mkdir.h"
+#include "dbus-unit.h"
+#include "dbus-job.h"
+#include "dbus-manager.h"
+#include "dbus-service.h"
+#include "dbus-socket.h"
+#include "dbus-target.h"
+#include "dbus-device.h"
+#include "dbus-mount.h"
+#include "dbus-automount.h"
+#include "dbus-snapshot.h"
+#include "dbus-swap.h"
+#include "dbus-timer.h"
+#include "dbus-path.h"
+#include "bus-errors.h"
+#include "special.h"
+#include "dbus-common.h"
+
+#define CONNECTIONS_MAX 52
+
+/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */
+#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
+/* Only used as a fallback */
+#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:"
+
+static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE;
+static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE;
+
+const char *const bus_interface_table[] = {
+ "org.freedesktop.DBus.Properties", bus_properties_interface,
+ "org.freedesktop.DBus.Introspectable", bus_introspectable_interface,
+ "org.freedesktop.systemd1.Manager", bus_manager_interface,
+ "org.freedesktop.systemd1.Job", bus_job_interface,
+ "org.freedesktop.systemd1.Unit", bus_unit_interface,
+ "org.freedesktop.systemd1.Service", bus_service_interface,
+ "org.freedesktop.systemd1.Socket", bus_socket_interface,
+ "org.freedesktop.systemd1.Target", bus_target_interface,
+ "org.freedesktop.systemd1.Device", bus_device_interface,
+ "org.freedesktop.systemd1.Mount", bus_mount_interface,
+ "org.freedesktop.systemd1.Automount", bus_automount_interface,
+ "org.freedesktop.systemd1.Snapshot", bus_snapshot_interface,
+ "org.freedesktop.systemd1.Swap", bus_swap_interface,
+ "org.freedesktop.systemd1.Timer", bus_timer_interface,
+ "org.freedesktop.systemd1.Path", bus_path_interface,
+ NULL
+};
+
+static void bus_done_api(Manager *m);
+static void bus_done_system(Manager *m);
+static void bus_done_private(Manager *m);
+static void shutdown_connection(Manager *m, DBusConnection *c);
+
+static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
+ Manager *m = data;
+
+ assert(bus);
+ assert(m);
+
+ /* We maintain two sets, one for those connections where we
+ * requested a dispatch, and another where we didn't. And then,
+ * we move the connections between the two sets. */
+
+ if (status == DBUS_DISPATCH_COMPLETE)
+ set_move_one(m->bus_connections, m->bus_connections_for_dispatch, bus);
+ else
+ set_move_one(m->bus_connections_for_dispatch, m->bus_connections, bus);
+}
+
+void bus_watch_event(Manager *m, Watch *w, int events) {
+ assert(m);
+ assert(w);
+
+ /* This is called by the event loop whenever there is
+ * something happening on D-Bus' file handles. */
+
+ if (!dbus_watch_get_enabled(w->data.bus_watch))
+ return;
+
+ dbus_watch_handle(w->data.bus_watch, bus_events_to_flags(events));
+}
+
+static dbus_bool_t bus_add_watch(DBusWatch *bus_watch, void *data) {
+ Manager *m = data;
+ Watch *w;
+ struct epoll_event ev;
+
+ assert(bus_watch);
+ assert(m);
+
+ if (!(w = new0(Watch, 1)))
+ return FALSE;
+
+ w->fd = dbus_watch_get_unix_fd(bus_watch);
+ w->type = WATCH_DBUS_WATCH;
+ w->data.bus_watch = bus_watch;
+
+ zero(ev);
+ ev.events = bus_flags_to_events(bus_watch);
+ ev.data.ptr = w;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
+
+ if (errno != EEXIST) {
+ free(w);
+ return FALSE;
+ }
+
+ /* Hmm, bloody D-Bus creates multiple watches on the
+ * same fd. epoll() does not like that. As a dirty
+ * hack we simply dup() the fd and hence get a second
+ * one we can safely add to the epoll(). */
+
+ if ((w->fd = dup(w->fd)) < 0) {
+ free(w);
+ return FALSE;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
+ close_nointr_nofail(w->fd);
+ free(w);
+ return FALSE;
+ }
+
+ w->fd_is_dupped = true;
+ }
+
+ dbus_watch_set_data(bus_watch, w, NULL);
+
+ return TRUE;
+}
+
+static void bus_remove_watch(DBusWatch *bus_watch, void *data) {
+ Manager *m = data;
+ Watch *w;
+
+ assert(bus_watch);
+ assert(m);
+
+ w = dbus_watch_get_data(bus_watch);
+ if (!w)
+ return;
+
+ assert(w->type == WATCH_DBUS_WATCH);
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+
+ if (w->fd_is_dupped)
+ close_nointr_nofail(w->fd);
+
+ free(w);
+}
+
+static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
+ Manager *m = data;
+ Watch *w;
+ struct epoll_event ev;
+
+ assert(bus_watch);
+ assert(m);
+
+ w = dbus_watch_get_data(bus_watch);
+ if (!w)
+ return;
+
+ assert(w->type == WATCH_DBUS_WATCH);
+
+ zero(ev);
+ ev.events = bus_flags_to_events(bus_watch);
+ ev.data.ptr = w;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_MOD, w->fd, &ev) == 0);
+}
+
+static int bus_timeout_arm(Manager *m, Watch *w) {
+ struct itimerspec its;
+
+ assert(m);
+ assert(w);
+
+ zero(its);
+
+ if (dbus_timeout_get_enabled(w->data.bus_timeout)) {
+ timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC);
+ its.it_interval = its.it_value;
+ }
+
+ if (timerfd_settime(w->fd, 0, &its, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+void bus_timeout_event(Manager *m, Watch *w, int events) {
+ assert(m);
+ assert(w);
+
+ /* This is called by the event loop whenever there is
+ * something happening on D-Bus' file handles. */
+
+ if (!(dbus_timeout_get_enabled(w->data.bus_timeout)))
+ return;
+
+ dbus_timeout_handle(w->data.bus_timeout);
+}
+
+static dbus_bool_t bus_add_timeout(DBusTimeout *timeout, void *data) {
+ Manager *m = data;
+ Watch *w;
+ struct epoll_event ev;
+
+ assert(timeout);
+ assert(m);
+
+ if (!(w = new0(Watch, 1)))
+ return FALSE;
+
+ if ((w->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
+ goto fail;
+
+ w->type = WATCH_DBUS_TIMEOUT;
+ w->data.bus_timeout = timeout;
+
+ if (bus_timeout_arm(m, w) < 0)
+ goto fail;
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = w;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
+ goto fail;
+
+ dbus_timeout_set_data(timeout, w, NULL);
+
+ return TRUE;
+
+fail:
+ if (w->fd >= 0)
+ close_nointr_nofail(w->fd);
+
+ free(w);
+ return FALSE;
+}
+
+static void bus_remove_timeout(DBusTimeout *timeout, void *data) {
+ Manager *m = data;
+ Watch *w;
+
+ assert(timeout);
+ assert(m);
+
+ w = dbus_timeout_get_data(timeout);
+ if (!w)
+ return;
+
+ assert(w->type == WATCH_DBUS_TIMEOUT);
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+ close_nointr_nofail(w->fd);
+ free(w);
+}
+
+static void bus_toggle_timeout(DBusTimeout *timeout, void *data) {
+ Manager *m = data;
+ Watch *w;
+ int r;
+
+ assert(timeout);
+ assert(m);
+
+ w = dbus_timeout_get_data(timeout);
+ if (!w)
+ return;
+
+ assert(w->type == WATCH_DBUS_TIMEOUT);
+
+ if ((r = bus_timeout_arm(m, w)) < 0)
+ log_error("Failed to rearm timer: %s", strerror(-r));
+}
+
+static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ DBusError error;
+ DBusMessage *reply = NULL;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ dbus_error_init(&error);
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
+ log_debug("Got D-Bus request: %s.%s() on %s",
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message),
+ dbus_message_get_path(message));
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+ log_debug("API D-Bus connection terminated.");
+ bus_done_api(m);
+
+ } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ const char *name, *old_owner, *new_owner;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse NameOwnerChanged message: %s", bus_error_message(&error));
+ else {
+ if (set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) name))
+ log_debug("Subscription client vanished: %s (left: %u)", name, set_size(BUS_CONNECTION_SUBSCRIBED(m, connection)));
+
+ if (old_owner[0] == 0)
+ old_owner = NULL;
+
+ if (new_owner[0] == 0)
+ new_owner = NULL;
+
+ manager_dispatch_bus_name_owner_changed(m, name, old_owner, new_owner);
+ }
+ } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Activator", "ActivationRequest")) {
+ const char *name;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse ActivationRequest message: %s", bus_error_message(&error));
+ else {
+ int r;
+ Unit *u;
+
+ log_debug("Got D-Bus activation request for %s", name);
+
+ if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) ||
+ manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) {
+ r = -EADDRNOTAVAIL;
+ dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
+ } else {
+ r = manager_load_unit(m, name, NULL, &error, &u);
+
+ if (r >= 0 && u->refuse_manual_start)
+ r = -EPERM;
+
+ if (r >= 0)
+ r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL);
+ }
+
+ if (r < 0) {
+ const char *id, *text;
+
+ log_debug("D-Bus activation failed for %s: %s", name, strerror(-r));
+
+ if (!(reply = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure")))
+ goto oom;
+
+ id = error.name ? error.name : bus_errno_to_dbus(r);
+ text = bus_error(&error, r);
+
+ if (!dbus_message_set_destination(reply, DBUS_SERVICE_DBUS) ||
+ !dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ goto oom;
+ }
+
+ /* On success we don't do anything, the service will be spawned now */
+ }
+ }
+
+ dbus_error_free(&error);
+
+ if (reply) {
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ DBusError error;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ dbus_error_init(&error);
+
+ if (m->api_bus != m->system_bus &&
+ (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL))
+ log_debug("Got D-Bus request on system bus: %s.%s() on %s",
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message),
+ dbus_message_get_path(message));
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+ log_debug("System D-Bus connection terminated.");
+ bus_done_system(m);
+
+ } else if (m->running_as != MANAGER_SYSTEM &&
+ dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
+
+ const char *cgroup;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &cgroup,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse Released message: %s", bus_error_message(&error));
+ else
+ cgroup_notify_empty(m, cgroup);
+ }
+
+ dbus_error_free(&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+ Manager *m = data;
+ DBusError error;
+
+ assert(connection);
+ assert(message);
+ assert(m);
+
+ dbus_error_init(&error);
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
+ dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
+ log_debug("Got D-Bus request: %s.%s() on %s",
+ dbus_message_get_interface(message),
+ dbus_message_get_member(message),
+ dbus_message_get_path(message));
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected"))
+ shutdown_connection(m, connection);
+ else if (m->running_as == MANAGER_SYSTEM &&
+ dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
+
+ const char *cgroup;
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &cgroup,
+ DBUS_TYPE_INVALID))
+ log_error("Failed to parse Released message: %s", bus_error_message(&error));
+ else
+ cgroup_notify_empty(m, cgroup);
+
+ /* Forward the message to the system bus, so that user
+ * instances are notified as well */
+
+ if (m->system_bus)
+ dbus_connection_send(m->system_bus, message, NULL);
+ }
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+unsigned bus_dispatch(Manager *m) {
+ DBusConnection *c;
+
+ assert(m);
+
+ if (m->queued_message) {
+ /* If we cannot get rid of this message we won't
+ * dispatch any D-Bus messages, so that we won't end
+ * up wanting to queue another message. */
+
+ if (m->queued_message_connection)
+ if (!dbus_connection_send(m->queued_message_connection, m->queued_message, NULL))
+ return 0;
+
+ dbus_message_unref(m->queued_message);
+ m->queued_message = NULL;
+ m->queued_message_connection = NULL;
+ }
+
+ if ((c = set_first(m->bus_connections_for_dispatch))) {
+ if (dbus_connection_dispatch(c) == DBUS_DISPATCH_COMPLETE)
+ set_move_one(m->bus_connections, m->bus_connections_for_dispatch, c);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) {
+ DBusMessage *reply;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ assert_se(reply = dbus_pending_call_steal_reply(pending));
+
+ switch (dbus_message_get_type(reply)) {
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+
+ assert_se(dbus_set_error_from_message(&error, reply));
+ log_warning("RequestName() failed: %s", bus_error_message(&error));
+ break;
+
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
+ uint32_t r;
+
+ if (!dbus_message_get_args(reply,
+ &error,
+ DBUS_TYPE_UINT32, &r,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse RequestName() reply: %s", bus_error_message(&error));
+ break;
+ }
+
+ if (r == 1)
+ log_debug("Successfully acquired name.");
+ else
+ log_error("Name already owned.");
+
+ break;
+ }
+
+ default:
+ assert_not_reached("Invalid reply message");
+ }
+
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
+}
+
+static int request_name(Manager *m) {
+ const char *name = "org.freedesktop.systemd1";
+ /* Allow replacing of our name, to ease implementation of
+ * reexecution, where we keep the old connection open until
+ * after the new connection is set up and the name installed
+ * to allow clients to synchronously wait for reexecution to
+ * finish */
+ uint32_t flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT|DBUS_NAME_FLAG_REPLACE_EXISTING;
+ DBusMessage *message = NULL;
+ DBusPendingCall *pending = NULL;
+
+ if (!(message = dbus_message_new_method_call(
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "RequestName")))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
+ goto oom;
+
+ if (!dbus_pending_call_set_notify(pending, request_name_pending_cb, m, NULL))
+ goto oom;
+
+ dbus_message_unref(message);
+ dbus_pending_call_unref(pending);
+
+ /* We simple ask for the name and don't wait for it. Sooner or
+ * later we'll have it. */
+
+ return 0;
+
+oom:
+ if (pending) {
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ }
+
+ if (message)
+ dbus_message_unref(message);
+
+ return -ENOMEM;
+}
+
+static void query_name_list_pending_cb(DBusPendingCall *pending, void *userdata) {
+ DBusMessage *reply;
+ DBusError error;
+ Manager *m = userdata;
+
+ assert(m);
+
+ dbus_error_init(&error);
+
+ assert_se(reply = dbus_pending_call_steal_reply(pending));
+
+ switch (dbus_message_get_type(reply)) {
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+
+ assert_se(dbus_set_error_from_message(&error, reply));
+ log_warning("ListNames() failed: %s", bus_error_message(&error));
+ break;
+
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
+ int r;
+ char **l;
+
+ if ((r = bus_parse_strv(reply, &l)) < 0)
+ log_warning("Failed to parse ListNames() reply: %s", strerror(-r));
+ else {
+ char **t;
+
+ STRV_FOREACH(t, l)
+ /* This is a bit hacky, we say the
+ * owner of the name is the name
+ * itself, because we don't want the
+ * extra traffic to figure out the
+ * real owner. */
+ manager_dispatch_bus_name_owner_changed(m, *t, NULL, *t);
+
+ strv_free(l);
+ }
+
+ break;
+ }
+
+ default:
+ assert_not_reached("Invalid reply message");
+ }
+
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
+}
+
+static int query_name_list(Manager *m) {
+ DBusMessage *message = NULL;
+ DBusPendingCall *pending = NULL;
+
+ /* Asks for the currently installed bus names */
+
+ if (!(message = dbus_message_new_method_call(
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "ListNames")))
+ goto oom;
+
+ if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
+ goto oom;
+
+ if (!dbus_pending_call_set_notify(pending, query_name_list_pending_cb, m, NULL))
+ goto oom;
+
+ dbus_message_unref(message);
+ dbus_pending_call_unref(pending);
+
+ /* We simple ask for the list and don't wait for it. Sooner or
+ * later we'll get it. */
+
+ return 0;
+
+oom:
+ if (pending) {
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ }
+
+ if (message)
+ dbus_message_unref(message);
+
+ return -ENOMEM;
+}
+
+static int bus_setup_loop(Manager *m, DBusConnection *bus) {
+ assert(m);
+ assert(bus);
+
+ dbus_connection_set_exit_on_disconnect(bus, FALSE);
+
+ if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
+ !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) {
+ log_error("Not enough memory");
+ return -ENOMEM;
+ }
+
+ if (set_put(m->bus_connections_for_dispatch, bus) < 0) {
+ log_error("Not enough memory");
+ return -ENOMEM;
+ }
+
+ dbus_connection_set_dispatch_status_function(bus, bus_dispatch_status, m, NULL);
+ return 0;
+}
+
+static dbus_bool_t allow_only_same_user(DBusConnection *connection, unsigned long uid, void *data) {
+ return uid == 0 || uid == geteuid();
+}
+
+static void bus_new_connection(
+ DBusServer *server,
+ DBusConnection *new_connection,
+ void *data) {
+
+ Manager *m = data;
+
+ assert(m);
+
+ if (set_size(m->bus_connections) >= CONNECTIONS_MAX) {
+ log_error("Too many concurrent connections.");
+ return;
+ }
+
+ dbus_connection_set_unix_user_function(new_connection, allow_only_same_user, NULL, NULL);
+
+ if (bus_setup_loop(m, new_connection) < 0)
+ return;
+
+ if (!dbus_connection_register_object_path(new_connection, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
+ !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
+ !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
+ !dbus_connection_add_filter(new_connection, private_bus_message_filter, m, NULL)) {
+ log_error("Not enough memory.");
+ return;
+ }
+
+ log_debug("Accepted connection on private bus.");
+
+ dbus_connection_ref(new_connection);
+}
+
+static int init_registered_system_bus(Manager *m) {
+ char *id;
+
+ if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
+ log_error("Not enough memory");
+ return -ENOMEM;
+ }
+
+ if (m->running_as != MANAGER_SYSTEM) {
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ dbus_bus_add_match(m->system_bus,
+ "type='signal',"
+ "interface='org.freedesktop.systemd1.Agent',"
+ "member='Released',"
+ "path='/org/freedesktop/systemd1/agent'",
+ &error);
+
+ if (dbus_error_is_set(&error)) {
+ log_error("Failed to register match: %s", bus_error_message(&error));
+ dbus_error_free(&error);
+ return -1;
+ }
+ }
+
+ log_debug("Successfully connected to system D-Bus bus %s as %s",
+ strnull((id = dbus_connection_get_server_id(m->system_bus))),
+ strnull(dbus_bus_get_unique_name(m->system_bus)));
+ dbus_free(id);
+
+ return 0;
+}
+
+static int init_registered_api_bus(Manager *m) {
+ int r;
+
+ if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
+ !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
+ !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
+ !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) {
+ log_error("Not enough memory");
+ return -ENOMEM;
+ }
+
+ /* Get NameOwnerChange messages */
+ dbus_bus_add_match(m->api_bus,
+ "type='signal',"
+ "sender='"DBUS_SERVICE_DBUS"',"
+ "interface='"DBUS_INTERFACE_DBUS"',"
+ "member='NameOwnerChanged',"
+ "path='"DBUS_PATH_DBUS"'",
+ NULL);
+
+ /* Get activation requests */
+ dbus_bus_add_match(m->api_bus,
+ "type='signal',"
+ "sender='"DBUS_SERVICE_DBUS"',"
+ "interface='org.freedesktop.systemd1.Activator',"
+ "member='ActivationRequest',"
+ "path='"DBUS_PATH_DBUS"'",
+ NULL);
+
+ r = request_name(m);
+ if (r < 0)
+ return r;
+
+ r = query_name_list(m);
+ if (r < 0)
+ return r;
+
+ if (m->running_as == MANAGER_USER) {
+ char *id;
+ log_debug("Successfully connected to API D-Bus bus %s as %s",
+ strnull((id = dbus_connection_get_server_id(m->api_bus))),
+ strnull(dbus_bus_get_unique_name(m->api_bus)));
+ dbus_free(id);
+ } else
+ log_debug("Successfully initialized API on the system bus");
+
+ return 0;
+}
+
+static void bus_register_cb(DBusPendingCall *pending, void *userdata) {
+ Manager *m = userdata;
+ DBusConnection **conn;
+ DBusMessage *reply;
+ DBusError error;
+ const char *name;
+ int r = 0;
+
+ dbus_error_init(&error);
+
+ conn = dbus_pending_call_get_data(pending, m->conn_data_slot);
+ assert(conn == &m->system_bus || conn == &m->api_bus);
+
+ reply = dbus_pending_call_steal_reply(pending);
+
+ switch (dbus_message_get_type(reply)) {
+ case DBUS_MESSAGE_TYPE_ERROR:
+ assert_se(dbus_set_error_from_message(&error, reply));
+ log_warning("Failed to register to bus: %s", bus_error_message(&error));
+ r = -1;
+ break;
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse Hello reply: %s", bus_error_message(&error));
+ r = -1;
+ break;
+ }
+
+ log_debug("Received name %s in reply to Hello", name);
+ if (!dbus_bus_set_unique_name(*conn, name)) {
+ log_error("Failed to set unique name");
+ r = -1;
+ break;
+ }
+
+ if (conn == &m->system_bus) {
+ r = init_registered_system_bus(m);
+ if (r == 0 && m->running_as == MANAGER_SYSTEM)
+ r = init_registered_api_bus(m);
+ } else
+ r = init_registered_api_bus(m);
+
+ break;
+ default:
+ assert_not_reached("Invalid reply message");
+ }
+
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
+
+ if (r < 0) {
+ if (conn == &m->system_bus) {
+ log_debug("Failed setting up the system bus");
+ bus_done_system(m);
+ } else {
+ log_debug("Failed setting up the API bus");
+ bus_done_api(m);
+ }
+ }
+}
+
+static int manager_bus_async_register(Manager *m, DBusConnection **conn) {
+ DBusMessage *message = NULL;
+ DBusPendingCall *pending = NULL;
+
+ message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "Hello");
+ if (!message)
+ goto oom;
+
+ if (!dbus_connection_send_with_reply(*conn, message, &pending, -1))
+ goto oom;
+
+ if (!dbus_pending_call_set_data(pending, m->conn_data_slot, conn, NULL))
+ goto oom;
+
+ if (!dbus_pending_call_set_notify(pending, bus_register_cb, m, NULL))
+ goto oom;
+
+ dbus_message_unref(message);
+ dbus_pending_call_unref(pending);
+
+ return 0;
+oom:
+ if (pending) {
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ }
+
+ if (message)
+ dbus_message_unref(message);
+
+ return -ENOMEM;
+}
+
+static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) {
+ const char *address;
+ DBusConnection *connection;
+ DBusError error;
+
+ switch (type) {
+ case DBUS_BUS_SYSTEM:
+ address = getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ if (!address || !address[0])
+ address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
+ break;
+ case DBUS_BUS_SESSION:
+ address = getenv("DBUS_SESSION_BUS_ADDRESS");
+ if (!address || !address[0])
+ address = DBUS_SESSION_BUS_DEFAULT_ADDRESS;
+ break;
+ default:
+ assert_not_reached("Invalid bus type");
+ }
+
+ dbus_error_init(&error);
+
+ connection = dbus_connection_open_private(address, &error);
+ if (!connection) {
+ log_warning("Failed to open private bus connection: %s", bus_error_message(&error));
+ goto fail;
+ }
+
+ return connection;
+fail:
+ if (connection)
+ dbus_connection_close(connection);
+ dbus_error_free(&error);
+ return NULL;
+}
+
+static int bus_init_system(Manager *m) {
+ int r;
+
+ if (m->system_bus)
+ return 0;
+
+ m->system_bus = manager_bus_connect_private(m, DBUS_BUS_SYSTEM);
+ if (!m->system_bus) {
+ log_debug("Failed to connect to system D-Bus, retrying later");
+ r = 0;
+ goto fail;
+ }
+
+ r = bus_setup_loop(m, m->system_bus);
+ if (r < 0)
+ goto fail;
+
+ r = manager_bus_async_register(m, &m->system_bus);
+ if (r < 0)
+ goto fail;
+
+ return 0;
+fail:
+ bus_done_system(m);
+
+ return r;
+}
+
+static int bus_init_api(Manager *m) {
+ int r;
+
+ if (m->api_bus)
+ return 0;
+
+ if (m->running_as == MANAGER_SYSTEM) {
+ m->api_bus = m->system_bus;
+ /* In this mode there is no distinct connection to the API bus,
+ * the API is published on the system bus.
+ * bus_register_cb() is aware of that and will init the API
+ * when the system bus gets registered.
+ * No need to setup anything here. */
+ return 0;
+ }
+
+ m->api_bus = manager_bus_connect_private(m, DBUS_BUS_SESSION);
+ if (!m->api_bus) {
+ log_debug("Failed to connect to API D-Bus, retrying later");
+ r = 0;
+ goto fail;
+ }
+
+ r = bus_setup_loop(m, m->api_bus);
+ if (r < 0)
+ goto fail;
+
+ r = manager_bus_async_register(m, &m->api_bus);
+ if (r < 0)
+ goto fail;
+
+ return 0;
+fail:
+ bus_done_api(m);
+
+ return r;
+}
+
+static int bus_init_private(Manager *m) {
+ DBusError error;
+ int r;
+ const char *const external_only[] = {
+ "EXTERNAL",
+ NULL
+ };
+
+ assert(m);
+
+ dbus_error_init(&error);
+
+ if (m->private_bus)
+ return 0;
+
+ if (m->running_as == MANAGER_SYSTEM) {
+
+ /* We want the private bus only when running as init */
+ if (getpid() != 1)
+ return 0;
+
+ unlink("/run/systemd/private");
+ m->private_bus = dbus_server_listen("unix:path=/run/systemd/private", &error);
+ } else {
+ const char *e;
+ char *p;
+
+ e = getenv("XDG_RUNTIME_DIR");
+ if (!e)
+ return 0;
+
+ if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) {
+ log_error("Not enough memory");
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ mkdir_parents(p+10, 0755);
+ unlink(p+10);
+ m->private_bus = dbus_server_listen(p, &error);
+ free(p);
+ }
+
+ if (!m->private_bus) {
+ log_error("Failed to create private D-Bus server: %s", bus_error_message(&error));
+ r = -EIO;
+ goto fail;
+ }
+
+ if (!dbus_server_set_auth_mechanisms(m->private_bus, (const char**) external_only) ||
+ !dbus_server_set_watch_functions(m->private_bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
+ !dbus_server_set_timeout_functions(m->private_bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) {
+ log_error("Not enough memory");
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ dbus_server_set_new_connection_function(m->private_bus, bus_new_connection, m, NULL);
+
+ log_debug("Successfully created private D-Bus server.");
+
+ return 0;
+
+fail:
+ bus_done_private(m);
+ dbus_error_free(&error);
+
+ return r;
+}
+
+int bus_init(Manager *m, bool try_bus_connect) {
+ int r;
+
+ if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 ||
+ set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0)
+ goto oom;
+
+ if (m->name_data_slot < 0)
+ if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot))
+ goto oom;
+
+ if (m->conn_data_slot < 0)
+ if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot))
+ goto oom;
+
+ if (m->subscribed_data_slot < 0)
+ if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot))
+ goto oom;
+
+ if (try_bus_connect) {
+ if ((r = bus_init_system(m)) < 0 ||
+ (r = bus_init_api(m)) < 0)
+ return r;
+ }
+
+ if ((r = bus_init_private(m)) < 0)
+ return r;
+
+ return 0;
+oom:
+ log_error("Not enough memory");
+ return -ENOMEM;
+}
+
+static void shutdown_connection(Manager *m, DBusConnection *c) {
+ Set *s;
+ Job *j;
+ Iterator i;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->bus == c) {
+ free(j->bus_client);
+ j->bus_client = NULL;
+
+ j->bus = NULL;
+ }
+
+ set_remove(m->bus_connections, c);
+ set_remove(m->bus_connections_for_dispatch, c);
+
+ if ((s = BUS_CONNECTION_SUBSCRIBED(m, c))) {
+ char *t;
+
+ while ((t = set_steal_first(s)))
+ free(t);
+
+ set_free(s);
+ }
+
+ if (m->queued_message_connection == c) {
+ m->queued_message_connection = NULL;
+
+ if (m->queued_message) {
+ dbus_message_unref(m->queued_message);
+ m->queued_message = NULL;
+ }
+ }
+
+ dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL);
+ /* system manager cannot afford to block on DBus */
+ if (m->running_as != MANAGER_SYSTEM)
+ dbus_connection_flush(c);
+ dbus_connection_close(c);
+ dbus_connection_unref(c);
+}
+
+static void bus_done_api(Manager *m) {
+ if (!m->api_bus)
+ return;
+
+ if (m->running_as == MANAGER_USER)
+ shutdown_connection(m, m->api_bus);
+
+ m->api_bus = NULL;
+
+ if (m->queued_message) {
+ dbus_message_unref(m->queued_message);
+ m->queued_message = NULL;
+ }
+}
+
+static void bus_done_system(Manager *m) {
+ if (!m->system_bus)
+ return;
+
+ if (m->running_as == MANAGER_SYSTEM)
+ bus_done_api(m);
+
+ shutdown_connection(m, m->system_bus);
+ m->system_bus = NULL;
+}
+
+static void bus_done_private(Manager *m) {
+ if (!m->private_bus)
+ return;
+
+ dbus_server_disconnect(m->private_bus);
+ dbus_server_unref(m->private_bus);
+ m->private_bus = NULL;
+}
+
+void bus_done(Manager *m) {
+ DBusConnection *c;
+
+ bus_done_api(m);
+ bus_done_system(m);
+ bus_done_private(m);
+
+ while ((c = set_steal_first(m->bus_connections)))
+ shutdown_connection(m, c);
+
+ while ((c = set_steal_first(m->bus_connections_for_dispatch)))
+ shutdown_connection(m, c);
+
+ set_free(m->bus_connections);
+ set_free(m->bus_connections_for_dispatch);
+
+ if (m->name_data_slot >= 0)
+ dbus_pending_call_free_data_slot(&m->name_data_slot);
+
+ if (m->conn_data_slot >= 0)
+ dbus_pending_call_free_data_slot(&m->conn_data_slot);
+
+ if (m->subscribed_data_slot >= 0)
+ dbus_connection_free_data_slot(&m->subscribed_data_slot);
+}
+
+static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) {
+ Manager *m = userdata;
+ DBusMessage *reply;
+ DBusError error;
+ const char *name;
+
+ dbus_error_init(&error);
+
+ assert_se(name = BUS_PENDING_CALL_NAME(m, pending));
+ assert_se(reply = dbus_pending_call_steal_reply(pending));
+
+ switch (dbus_message_get_type(reply)) {
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+
+ assert_se(dbus_set_error_from_message(&error, reply));
+ log_warning("GetConnectionUnixProcessID() failed: %s", bus_error_message(&error));
+ break;
+
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
+ uint32_t r;
+
+ if (!dbus_message_get_args(reply,
+ &error,
+ DBUS_TYPE_UINT32, &r,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse GetConnectionUnixProcessID() reply: %s", bus_error_message(&error));
+ break;
+ }
+
+ manager_dispatch_bus_query_pid_done(m, name, (pid_t) r);
+ break;
+ }
+
+ default:
+ assert_not_reached("Invalid reply message");
+ }
+
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
+}
+
+int bus_query_pid(Manager *m, const char *name) {
+ DBusMessage *message = NULL;
+ DBusPendingCall *pending = NULL;
+ char *n = NULL;
+
+ assert(m);
+ assert(name);
+
+ if (!(message = dbus_message_new_method_call(
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID")))
+ goto oom;
+
+ if (!(dbus_message_append_args(
+ message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)))
+ goto oom;
+
+ if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
+ goto oom;
+
+ if (!(n = strdup(name)))
+ goto oom;
+
+ if (!dbus_pending_call_set_data(pending, m->name_data_slot, n, free))
+ goto oom;
+
+ n = NULL;
+
+ if (!dbus_pending_call_set_notify(pending, query_pid_pending_cb, m, NULL))
+ goto oom;
+
+ dbus_message_unref(message);
+ dbus_pending_call_unref(pending);
+
+ return 0;
+
+oom:
+ free(n);
+
+ if (pending) {
+ dbus_pending_call_cancel(pending);
+ dbus_pending_call_unref(pending);
+ }
+
+ if (message)
+ dbus_message_unref(message);
+
+ return -ENOMEM;
+}
+
+int bus_broadcast(Manager *m, DBusMessage *message) {
+ bool oom = false;
+ Iterator i;
+ DBusConnection *c;
+
+ assert(m);
+ assert(message);
+
+ SET_FOREACH(c, m->bus_connections_for_dispatch, i)
+ if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
+ oom = !dbus_connection_send(c, message, NULL);
+
+ SET_FOREACH(c, m->bus_connections, i)
+ if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
+ oom = !dbus_connection_send(c, message, NULL);
+
+ return oom ? -ENOMEM : 0;
+}
+
+bool bus_has_subscriber(Manager *m) {
+ Iterator i;
+ DBusConnection *c;
+
+ assert(m);
+
+ SET_FOREACH(c, m->bus_connections_for_dispatch, i)
+ if (bus_connection_has_subscriber(m, c))
+ return true;
+
+ SET_FOREACH(c, m->bus_connections, i)
+ if (bus_connection_has_subscriber(m, c))
+ return true;
+
+ return false;
+}
+
+bool bus_connection_has_subscriber(Manager *m, DBusConnection *c) {
+ assert(m);
+ assert(c);
+
+ return !set_isempty(BUS_CONNECTION_SUBSCRIBED(m, c));
+}
+
+int bus_fdset_add_all(Manager *m, FDSet *fds) {
+ Iterator i;
+ DBusConnection *c;
+
+ assert(m);
+ assert(fds);
+
+ /* When we are about to reexecute we add all D-Bus fds to the
+ * set to pass over to the newly executed systemd. They won't
+ * be used there however, except that they are closed at the
+ * very end of deserialization, those making it possible for
+ * clients to synchronously wait for systemd to reexec by
+ * simply waiting for disconnection */
+
+ SET_FOREACH(c, m->bus_connections_for_dispatch, i) {
+ int fd;
+
+ if (dbus_connection_get_unix_fd(c, &fd)) {
+ fd = fdset_put_dup(fds, fd);
+
+ if (fd < 0)
+ return fd;
+ }
+ }
+
+ SET_FOREACH(c, m->bus_connections, i) {
+ int fd;
+
+ if (dbus_connection_get_unix_fd(c, &fd)) {
+ fd = fdset_put_dup(fds, fd);
+
+ if (fd < 0)
+ return fd;
+ }
+ }
+
+ return 0;
+}
+
+void bus_broadcast_finished(
+ Manager *m,
+ usec_t kernel_usec,
+ usec_t initrd_usec,
+ usec_t userspace_usec,
+ usec_t total_usec) {
+
+ DBusMessage *message;
+
+ assert(m);
+
+ message = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished");
+ if (!message) {
+ log_error("Out of memory.");
+ return;
+ }
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+ if (!dbus_message_append_args(message,
+ DBUS_TYPE_UINT64, &kernel_usec,
+ DBUS_TYPE_UINT64, &initrd_usec,
+ DBUS_TYPE_UINT64, &userspace_usec,
+ DBUS_TYPE_UINT64, &total_usec,
+ DBUS_TYPE_INVALID)) {
+ log_error("Out of memory.");
+ goto finish;
+ }
+
+
+ if (bus_broadcast(m, message) < 0) {
+ log_error("Out of memory.");
+ goto finish;
+ }
+
+finish:
+ if (message)
+ dbus_message_unref(message);
+}
diff --git a/src/core/dbus.h b/src/core/dbus.h
new file mode 100644
index 0000000..bd539d0
--- /dev/null
+++ b/src/core/dbus.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodbushfoo
+#define foodbushfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "manager.h"
+
+int bus_init(Manager *m, bool try_bus_connect);
+void bus_done(Manager *m);
+
+unsigned bus_dispatch(Manager *m);
+
+void bus_watch_event(Manager *m, Watch *w, int events);
+void bus_timeout_event(Manager *m, Watch *w, int events);
+
+int bus_query_pid(Manager *m, const char *name);
+
+int bus_broadcast(Manager *m, DBusMessage *message);
+
+bool bus_has_subscriber(Manager *m);
+bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
+
+int bus_fdset_add_all(Manager *m, FDSet *fds);
+
+void bus_broadcast_finished(Manager *m, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
+
+#define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
+#define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
+
+extern const char * const bus_interface_table[];
+
+#endif
diff --git a/src/core/device.c b/src/core/device.c
new file mode 100644
index 0000000..0575379
--- /dev/null
+++ b/src/core/device.c
@@ -0,0 +1,616 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/epoll.h>
+#include <libudev.h>
+
+#include "unit.h"
+#include "device.h"
+#include "strv.h"
+#include "log.h"
+#include "unit-name.h"
+#include "dbus-device.h"
+#include "def.h"
+
+static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
+ [DEVICE_DEAD] = UNIT_INACTIVE,
+ [DEVICE_PLUGGED] = UNIT_ACTIVE
+};
+
+static void device_unset_sysfs(Device *d) {
+ Device *first;
+
+ assert(d);
+
+ if (!d->sysfs)
+ return;
+
+ /* Remove this unit from the chain of devices which share the
+ * same sysfs path. */
+ first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
+ LIST_REMOVE(Device, same_sysfs, first, d);
+
+ if (first)
+ hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first);
+ else
+ hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
+
+ free(d->sysfs);
+ d->sysfs = NULL;
+}
+
+static void device_init(Unit *u) {
+ Device *d = DEVICE(u);
+
+ assert(d);
+ assert(UNIT(d)->load_state == UNIT_STUB);
+
+ /* In contrast to all other unit types we timeout jobs waiting
+ * for devices by default. This is because they otherwise wait
+ * indefinitely for plugged in devices, something which cannot
+ * happen for the other units since their operations time out
+ * anyway. */
+ UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC;
+
+ UNIT(d)->ignore_on_isolate = true;
+ UNIT(d)->ignore_on_snapshot = true;
+}
+
+static void device_done(Unit *u) {
+ Device *d = DEVICE(u);
+
+ assert(d);
+
+ device_unset_sysfs(d);
+}
+
+static void device_set_state(Device *d, DeviceState state) {
+ DeviceState old_state;
+ assert(d);
+
+ old_state = d->state;
+ d->state = state;
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(d)->id,
+ device_state_to_string(old_state),
+ device_state_to_string(state));
+
+ unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int device_coldplug(Unit *u) {
+ Device *d = DEVICE(u);
+
+ assert(d);
+ assert(d->state == DEVICE_DEAD);
+
+ if (d->sysfs)
+ device_set_state(d, DEVICE_PLUGGED);
+
+ return 0;
+}
+
+static void device_dump(Unit *u, FILE *f, const char *prefix) {
+ Device *d = DEVICE(u);
+
+ assert(d);
+
+ fprintf(f,
+ "%sDevice State: %s\n"
+ "%sSysfs Path: %s\n",
+ prefix, device_state_to_string(d->state),
+ prefix, strna(d->sysfs));
+}
+
+static UnitActiveState device_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[DEVICE(u)->state];
+}
+
+static const char *device_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return device_state_to_string(DEVICE(u)->state);
+}
+
+static int device_add_escaped_name(Unit *u, const char *dn) {
+ char *e;
+ int r;
+
+ assert(u);
+ assert(dn);
+ assert(dn[0] == '/');
+
+ if (!(e = unit_name_from_path(dn, ".device")))
+ return -ENOMEM;
+
+ r = unit_add_name(u, e);
+ free(e);
+
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ return 0;
+}
+
+static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
+ char *e;
+ Unit *u;
+
+ assert(m);
+ assert(dn);
+ assert(dn[0] == '/');
+ assert(_u);
+
+ if (!(e = unit_name_from_path(dn, ".device")))
+ return -ENOMEM;
+
+ u = manager_get_unit(m, e);
+ free(e);
+
+ if (u) {
+ *_u = u;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
+ const char *sysfs, *model;
+ Unit *u = NULL;
+ int r;
+ bool delete;
+
+ assert(m);
+
+ if (!(sysfs = udev_device_get_syspath(dev)))
+ return -ENOMEM;
+
+ if ((r = device_find_escape_name(m, path, &u)) < 0)
+ return r;
+
+ if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
+ return -EEXIST;
+
+ if (!u) {
+ delete = true;
+
+ u = unit_new(m, sizeof(Device));
+ if (!u)
+ return -ENOMEM;
+
+ r = device_add_escaped_name(u, path);
+ if (r < 0)
+ goto fail;
+
+ unit_add_to_load_queue(u);
+ } else
+ delete = false;
+
+ /* If this was created via some dependency and has not
+ * actually been seen yet ->sysfs will not be
+ * initialized. Hence initialize it if necessary. */
+
+ if (!DEVICE(u)->sysfs) {
+ Device *first;
+
+ if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!m->devices_by_sysfs)
+ if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ first = hashmap_get(m->devices_by_sysfs, sysfs);
+ LIST_PREPEND(Device, same_sysfs, first, DEVICE(u));
+
+ if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0)
+ goto fail;
+ }
+
+ if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
+ (model = udev_device_get_property_value(dev, "ID_MODEL"))) {
+ if ((r = unit_set_description(u, model)) < 0)
+ goto fail;
+ } else
+ if ((r = unit_set_description(u, path)) < 0)
+ goto fail;
+
+ if (main) {
+ /* The additional systemd udev properties we only
+ * interpret for the main object */
+ const char *wants, *alias;
+
+ if ((alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"))) {
+ if (!is_path(alias))
+ log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias);
+ else {
+ if ((r = device_add_escaped_name(u, alias)) < 0)
+ goto fail;
+ }
+ }
+
+ if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) {
+ char *state, *w;
+ size_t l;
+
+ FOREACH_WORD_QUOTED(w, l, wants, state) {
+ char *e;
+
+ if (!(e = strndup(w, l))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
+ free(e);
+
+ if (r < 0)
+ goto fail;
+ }
+ }
+ }
+
+ unit_add_to_dbus_queue(u);
+ return 0;
+
+fail:
+ log_warning("Failed to load device unit: %s", strerror(-r));
+
+ if (delete && u)
+ unit_free(u);
+
+ return r;
+}
+
+static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
+ const char *sysfs, *dn;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ assert(m);
+
+ if (!(sysfs = udev_device_get_syspath(dev)))
+ return -ENOMEM;
+
+ /* Add the main unit named after the sysfs path */
+ device_update_unit(m, dev, sysfs, true);
+
+ /* Add an additional unit for the device node */
+ if ((dn = udev_device_get_devnode(dev)))
+ device_update_unit(m, dev, dn, false);
+
+ /* Add additional units for all symlinks */
+ first = udev_device_get_devlinks_list_entry(dev);
+ udev_list_entry_foreach(item, first) {
+ const char *p;
+ struct stat st;
+
+ /* Don't bother with the /dev/block links */
+ p = udev_list_entry_get_name(item);
+
+ if (path_startswith(p, "/dev/block/") ||
+ path_startswith(p, "/dev/char/"))
+ continue;
+
+ /* Verify that the symlink in the FS actually belongs
+ * to this device. This is useful to deal with
+ * conflicting devices, e.g. when two disks want the
+ * same /dev/disk/by-label/xxx link because they have
+ * the same label. We want to make sure that the same
+ * device that won the symlink wins in systemd, so we
+ * check the device node major/minor*/
+ if (stat(p, &st) >= 0)
+ if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
+ st.st_rdev != udev_device_get_devnum(dev))
+ continue;
+
+ device_update_unit(m, dev, p, false);
+ }
+
+ if (update_state) {
+ Device *d, *l;
+
+ manager_dispatch_load_queue(m);
+
+ l = hashmap_get(m->devices_by_sysfs, sysfs);
+ LIST_FOREACH(same_sysfs, d, l)
+ device_set_state(d, DEVICE_PLUGGED);
+ }
+
+ return 0;
+}
+
+static int device_process_path(Manager *m, const char *path, bool update_state) {
+ int r;
+ struct udev_device *dev;
+
+ assert(m);
+ assert(path);
+
+ if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
+ log_warning("Failed to get udev device object from udev for path %s.", path);
+ return -ENOMEM;
+ }
+
+ r = device_process_new_device(m, dev, update_state);
+ udev_device_unref(dev);
+ return r;
+}
+
+static int device_process_removed_device(Manager *m, struct udev_device *dev) {
+ const char *sysfs;
+ Device *d;
+
+ assert(m);
+ assert(dev);
+
+ if (!(sysfs = udev_device_get_syspath(dev)))
+ return -ENOMEM;
+
+ /* Remove all units of this sysfs path */
+ while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
+ device_unset_sysfs(d);
+ device_set_state(d, DEVICE_DEAD);
+ }
+
+ return 0;
+}
+
+static Unit *device_following(Unit *u) {
+ Device *d = DEVICE(u);
+ Device *other, *first = NULL;
+
+ assert(d);
+
+ if (startswith(u->id, "sys-"))
+ return NULL;
+
+ /* Make everybody follow the unit that's named after the sysfs path */
+ for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
+ if (startswith(UNIT(other)->id, "sys-"))
+ return UNIT(other);
+
+ for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) {
+ if (startswith(UNIT(other)->id, "sys-"))
+ return UNIT(other);
+
+ first = other;
+ }
+
+ return UNIT(first);
+}
+
+static int device_following_set(Unit *u, Set **_s) {
+ Device *d = DEVICE(u);
+ Device *other;
+ Set *s;
+ int r;
+
+ assert(d);
+ assert(_s);
+
+ if (!d->same_sysfs_prev && !d->same_sysfs_next) {
+ *_s = NULL;
+ return 0;
+ }
+
+ if (!(s = set_new(NULL, NULL)))
+ return -ENOMEM;
+
+ for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
+ if ((r = set_put(s, other)) < 0)
+ goto fail;
+
+ for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev)
+ if ((r = set_put(s, other)) < 0)
+ goto fail;
+
+ *_s = s;
+ return 1;
+
+fail:
+ set_free(s);
+ return r;
+}
+
+static void device_shutdown(Manager *m) {
+ assert(m);
+
+ if (m->udev_monitor) {
+ udev_monitor_unref(m->udev_monitor);
+ m->udev_monitor = NULL;
+ }
+
+ if (m->udev) {
+ udev_unref(m->udev);
+ m->udev = NULL;
+ }
+
+ hashmap_free(m->devices_by_sysfs);
+ m->devices_by_sysfs = NULL;
+}
+
+static int device_enumerate(Manager *m) {
+ struct epoll_event ev;
+ int r;
+ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ assert(m);
+
+ if (!m->udev) {
+ if (!(m->udev = udev_new()))
+ return -ENOMEM;
+
+ if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* This will fail if we are unprivileged, but that
+ * should not matter much, as user instances won't run
+ * during boot. */
+ udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
+
+ if (udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd") < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ m->udev_watch.type = WATCH_UDEV;
+ m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = &m->udev_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
+ return -errno;
+ }
+
+ if (!(e = udev_enumerate_new(m->udev))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ if (udev_enumerate_add_match_tag(e, "systemd") < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (udev_enumerate_scan_devices(e) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first)
+ device_process_path(m, udev_list_entry_get_name(item), false);
+
+ udev_enumerate_unref(e);
+ return 0;
+
+fail:
+ if (e)
+ udev_enumerate_unref(e);
+
+ device_shutdown(m);
+ return r;
+}
+
+void device_fd_event(Manager *m, int events) {
+ struct udev_device *dev;
+ int r;
+ const char *action, *ready;
+
+ assert(m);
+
+ if (events != EPOLLIN) {
+ static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5);
+
+ if (!ratelimit_test(&limit))
+ log_error("Failed to get udev event: %m");
+ if (!(events & EPOLLIN))
+ return;
+ }
+
+ if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
+ /*
+ * libudev might filter-out devices which pass the bloom filter,
+ * so getting NULL here is not necessarily an error
+ */
+ return;
+ }
+
+ if (!(action = udev_device_get_action(dev))) {
+ log_error("Failed to get udev action string.");
+ goto fail;
+ }
+
+ ready = udev_device_get_property_value(dev, "SYSTEMD_READY");
+
+ if (streq(action, "remove") || (ready && parse_boolean(ready) == 0)) {
+ if ((r = device_process_removed_device(m, dev)) < 0) {
+ log_error("Failed to process udev device event: %s", strerror(-r));
+ goto fail;
+ }
+ } else {
+ if ((r = device_process_new_device(m, dev, true)) < 0) {
+ log_error("Failed to process udev device event: %s", strerror(-r));
+ goto fail;
+ }
+ }
+
+fail:
+ udev_device_unref(dev);
+}
+
+static const char* const device_state_table[_DEVICE_STATE_MAX] = {
+ [DEVICE_DEAD] = "dead",
+ [DEVICE_PLUGGED] = "plugged"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
+
+const UnitVTable device_vtable = {
+ .suffix = ".device",
+ .object_size = sizeof(Device),
+ .sections =
+ "Unit\0"
+ "Device\0"
+ "Install\0",
+
+ .no_instances = true,
+
+ .init = device_init,
+
+ .load = unit_load_fragment_and_dropin_optional,
+ .done = device_done,
+ .coldplug = device_coldplug,
+
+ .dump = device_dump,
+
+ .active_state = device_active_state,
+ .sub_state_to_string = device_sub_state_to_string,
+
+ .bus_interface = "org.freedesktop.systemd1.Device",
+ .bus_message_handler = bus_device_message_handler,
+ .bus_invalidating_properties = bus_device_invalidating_properties,
+
+ .following = device_following,
+ .following_set = device_following_set,
+
+ .enumerate = device_enumerate,
+ .shutdown = device_shutdown
+};
diff --git a/src/core/device.h b/src/core/device.h
new file mode 100644
index 0000000..a05c3d3
--- /dev/null
+++ b/src/core/device.h
@@ -0,0 +1,59 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foodevicehfoo
+#define foodevicehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Device Device;
+
+#include "unit.h"
+
+/* We simply watch devices, we cannot plug/unplug them. That
+ * simplifies the state engine greatly */
+typedef enum DeviceState {
+ DEVICE_DEAD,
+ DEVICE_PLUGGED,
+ _DEVICE_STATE_MAX,
+ _DEVICE_STATE_INVALID = -1
+} DeviceState;
+
+struct Device {
+ Unit meta;
+
+ char *sysfs;
+
+ /* In order to be able to distinguish dependencies on
+ different device nodes we might end up creating multiple
+ devices for the same sysfs path. We chain them up here. */
+
+ LIST_FIELDS(struct Device, same_sysfs);
+
+ DeviceState state;
+};
+
+extern const UnitVTable device_vtable;
+
+void device_fd_event(Manager *m, int events);
+
+const char* device_state_to_string(DeviceState i);
+DeviceState device_state_from_string(const char *s);
+
+#endif
diff --git a/src/core/execute.c b/src/core/execute.c
new file mode 100644
index 0000000..179ca7e
--- /dev/null
+++ b/src/core/execute.c
@@ -0,0 +1,2113 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/prctl.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <linux/oom.h>
+
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#include "execute.h"
+#include "strv.h"
+#include "macro.h"
+#include "capability.h"
+#include "util.h"
+#include "log.h"
+#include "ioprio.h"
+#include "securebits.h"
+#include "cgroup.h"
+#include "namespace.h"
+#include "tcpwrap.h"
+#include "exit-status.h"
+#include "missing.h"
+#include "utmp-wtmp.h"
+#include "def.h"
+#include "loopback-setup.h"
+
+/* This assumes there is a 'tty' group */
+#define TTY_MODE 0620
+
+static int shift_fds(int fds[], unsigned n_fds) {
+ int start, restart_from;
+
+ if (n_fds <= 0)
+ return 0;
+
+ /* Modifies the fds array! (sorts it) */
+
+ assert(fds);
+
+ start = 0;
+ for (;;) {
+ int i;
+
+ restart_from = -1;
+
+ for (i = start; i < (int) n_fds; i++) {
+ int nfd;
+
+ /* Already at right index? */
+ if (fds[i] == i+3)
+ continue;
+
+ if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
+ return -errno;
+
+ close_nointr_nofail(fds[i]);
+ fds[i] = nfd;
+
+ /* Hmm, the fd we wanted isn't free? Then
+ * let's remember that and try again from here*/
+ if (nfd != i+3 && restart_from < 0)
+ restart_from = i;
+ }
+
+ if (restart_from < 0)
+ break;
+
+ start = restart_from;
+ }
+
+ return 0;
+}
+
+static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
+ unsigned i;
+ int r;
+
+ if (n_fds <= 0)
+ return 0;
+
+ assert(fds);
+
+ /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
+
+ for (i = 0; i < n_fds; i++) {
+
+ if ((r = fd_nonblock(fds[i], nonblock)) < 0)
+ return r;
+
+ /* We unconditionally drop FD_CLOEXEC from the fds,
+ * since after all we want to pass these fds to our
+ * children */
+
+ if ((r = fd_cloexec(fds[i], false)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static const char *tty_path(const ExecContext *context) {
+ assert(context);
+
+ if (context->tty_path)
+ return context->tty_path;
+
+ return "/dev/console";
+}
+
+void exec_context_tty_reset(const ExecContext *context) {
+ assert(context);
+
+ if (context->tty_vhangup)
+ terminal_vhangup(tty_path(context));
+
+ if (context->tty_reset)
+ reset_terminal(tty_path(context));
+
+ if (context->tty_vt_disallocate && context->tty_path)
+ vt_disallocate(context->tty_path);
+}
+
+static int open_null_as(int flags, int nfd) {
+ int fd, r;
+
+ assert(nfd >= 0);
+
+ if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
+ return -errno;
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+
+static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, int nfd) {
+ int fd, r;
+ union sockaddr_union sa;
+
+ assert(context);
+ assert(output < _EXEC_OUTPUT_MAX);
+ assert(ident);
+ assert(nfd >= 0);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -errno;
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
+
+ r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+ if (r < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (shutdown(fd, SHUT_RD) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ dprintf(fd,
+ "%s\n"
+ "%i\n"
+ "%i\n"
+ "%i\n"
+ "%i\n"
+ "%i\n",
+ context->syslog_identifier ? context->syslog_identifier : ident,
+ context->syslog_priority,
+ !!context->syslog_level_prefix,
+ output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
+ output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+static int open_terminal_as(const char *path, mode_t mode, int nfd) {
+ int fd, r;
+
+ assert(path);
+ assert(nfd >= 0);
+
+ if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
+ return fd;
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+
+static bool is_terminal_input(ExecInput i) {
+ return
+ i == EXEC_INPUT_TTY ||
+ i == EXEC_INPUT_TTY_FORCE ||
+ i == EXEC_INPUT_TTY_FAIL;
+}
+
+static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
+
+ if (is_terminal_input(std_input) && !apply_tty_stdin)
+ return EXEC_INPUT_NULL;
+
+ if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
+ return EXEC_INPUT_NULL;
+
+ return std_input;
+}
+
+static int fixup_output(ExecOutput std_output, int socket_fd) {
+
+ if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
+ return EXEC_OUTPUT_INHERIT;
+
+ return std_output;
+}
+
+static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
+ ExecInput i;
+
+ assert(context);
+
+ i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+
+ switch (i) {
+
+ case EXEC_INPUT_NULL:
+ return open_null_as(O_RDONLY, STDIN_FILENO);
+
+ case EXEC_INPUT_TTY:
+ case EXEC_INPUT_TTY_FORCE:
+ case EXEC_INPUT_TTY_FAIL: {
+ int fd, r;
+
+ if ((fd = acquire_terminal(
+ tty_path(context),
+ i == EXEC_INPUT_TTY_FAIL,
+ i == EXEC_INPUT_TTY_FORCE,
+ false)) < 0)
+ return fd;
+
+ if (fd != STDIN_FILENO) {
+ r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+ close_nointr_nofail(fd);
+ } else
+ r = STDIN_FILENO;
+
+ return r;
+ }
+
+ case EXEC_INPUT_SOCKET:
+ return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+
+ default:
+ assert_not_reached("Unknown input type");
+ }
+}
+
+static int setup_output(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) {
+ ExecOutput o;
+ ExecInput i;
+
+ assert(context);
+ assert(ident);
+
+ i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ o = fixup_output(context->std_output, socket_fd);
+
+ /* This expects the input is already set up */
+
+ switch (o) {
+
+ case EXEC_OUTPUT_INHERIT:
+
+ /* If input got downgraded, inherit the original value */
+ if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
+ return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+
+ /* If the input is connected to anything that's not a /dev/null, inherit that... */
+ if (i != EXEC_INPUT_NULL)
+ return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
+ if (getppid() != 1)
+ return STDOUT_FILENO;
+
+ /* We need to open /dev/null here anew, to get the
+ * right access mode. So we fall through */
+
+ case EXEC_OUTPUT_NULL:
+ return open_null_as(O_WRONLY, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_TTY:
+ if (is_terminal_input(i))
+ return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ /* We don't reset the terminal if this is just about output */
+ return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_SYSLOG:
+ case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
+ case EXEC_OUTPUT_KMSG:
+ case EXEC_OUTPUT_KMSG_AND_CONSOLE:
+ case EXEC_OUTPUT_JOURNAL:
+ case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
+ return connect_logger_as(context, o, ident, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ default:
+ assert_not_reached("Unknown output type");
+ }
+}
+
+static int setup_error(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) {
+ ExecOutput o, e;
+ ExecInput i;
+
+ assert(context);
+ assert(ident);
+
+ i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ o = fixup_output(context->std_output, socket_fd);
+ e = fixup_output(context->std_error, socket_fd);
+
+ /* This expects the input and output are already set up */
+
+ /* Don't change the stderr file descriptor if we inherit all
+ * the way and are not on a tty */
+ if (e == EXEC_OUTPUT_INHERIT &&
+ o == EXEC_OUTPUT_INHERIT &&
+ i == EXEC_INPUT_NULL &&
+ !is_terminal_input(context->std_input) &&
+ getppid () != 1)
+ return STDERR_FILENO;
+
+ /* Duplicate from stdout if possible */
+ if (e == o || e == EXEC_OUTPUT_INHERIT)
+ return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ switch (e) {
+
+ case EXEC_OUTPUT_NULL:
+ return open_null_as(O_WRONLY, STDERR_FILENO);
+
+ case EXEC_OUTPUT_TTY:
+ if (is_terminal_input(i))
+ return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ /* We don't reset the terminal if this is just about output */
+ return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
+
+ case EXEC_OUTPUT_SYSLOG:
+ case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
+ case EXEC_OUTPUT_KMSG:
+ case EXEC_OUTPUT_KMSG_AND_CONSOLE:
+ case EXEC_OUTPUT_JOURNAL:
+ case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
+ return connect_logger_as(context, e, ident, STDERR_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ default:
+ assert_not_reached("Unknown error type");
+ }
+}
+
+static int chown_terminal(int fd, uid_t uid) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ /* This might fail. What matters are the results. */
+ (void) fchown(fd, uid, -1);
+ (void) fchmod(fd, TTY_MODE);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
+ return -EPERM;
+
+ return 0;
+}
+
+static int setup_confirm_stdio(const ExecContext *context,
+ int *_saved_stdin,
+ int *_saved_stdout) {
+ int fd = -1, saved_stdin, saved_stdout = -1, r;
+
+ assert(context);
+ assert(_saved_stdin);
+ assert(_saved_stdout);
+
+ /* This returns positive EXIT_xxx return values instead of
+ * negative errno style values! */
+
+ if ((saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3)) < 0)
+ return EXIT_STDIN;
+
+ if ((saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3)) < 0) {
+ r = EXIT_STDOUT;
+ goto fail;
+ }
+
+ if ((fd = acquire_terminal(
+ tty_path(context),
+ context->std_input == EXEC_INPUT_TTY_FAIL,
+ context->std_input == EXEC_INPUT_TTY_FORCE,
+ false)) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (chown_terminal(fd, getuid()) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (dup2(fd, STDIN_FILENO) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (dup2(fd, STDOUT_FILENO) < 0) {
+ r = EXIT_STDOUT;
+ goto fail;
+ }
+
+ if (fd >= 2)
+ close_nointr_nofail(fd);
+
+ *_saved_stdin = saved_stdin;
+ *_saved_stdout = saved_stdout;
+
+ return 0;
+
+fail:
+ if (saved_stdout >= 0)
+ close_nointr_nofail(saved_stdout);
+
+ if (saved_stdin >= 0)
+ close_nointr_nofail(saved_stdin);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+static int restore_confirm_stdio(const ExecContext *context,
+ int *saved_stdin,
+ int *saved_stdout,
+ bool *keep_stdin,
+ bool *keep_stdout) {
+
+ assert(context);
+ assert(saved_stdin);
+ assert(*saved_stdin >= 0);
+ assert(saved_stdout);
+ assert(*saved_stdout >= 0);
+
+ /* This returns positive EXIT_xxx return values instead of
+ * negative errno style values! */
+
+ if (is_terminal_input(context->std_input)) {
+
+ /* The service wants terminal input. */
+
+ *keep_stdin = true;
+ *keep_stdout =
+ context->std_output == EXEC_OUTPUT_INHERIT ||
+ context->std_output == EXEC_OUTPUT_TTY;
+
+ } else {
+ /* If the service doesn't want a controlling terminal,
+ * then we need to get rid entirely of what we have
+ * already. */
+
+ if (release_terminal() < 0)
+ return EXIT_STDIN;
+
+ if (dup2(*saved_stdin, STDIN_FILENO) < 0)
+ return EXIT_STDIN;
+
+ if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
+ return EXIT_STDOUT;
+
+ *keep_stdout = *keep_stdin = false;
+ }
+
+ return 0;
+}
+
+static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
+ bool keep_groups = false;
+ int r;
+
+ assert(context);
+
+ /* Lookup and set GID and supplementary group list. Here too
+ * we avoid NSS lookups for gid=0. */
+
+ if (context->group || username) {
+
+ if (context->group) {
+ const char *g = context->group;
+
+ if ((r = get_group_creds(&g, &gid)) < 0)
+ return r;
+ }
+
+ /* First step, initialize groups from /etc/groups */
+ if (username && gid != 0) {
+ if (initgroups(username, gid) < 0)
+ return -errno;
+
+ keep_groups = true;
+ }
+
+ /* Second step, set our gids */
+ if (setresgid(gid, gid, gid) < 0)
+ return -errno;
+ }
+
+ if (context->supplementary_groups) {
+ int ngroups_max, k;
+ gid_t *gids;
+ char **i;
+
+ /* Final step, initialize any manually set supplementary groups */
+ assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
+
+ if (!(gids = new(gid_t, ngroups_max)))
+ return -ENOMEM;
+
+ if (keep_groups) {
+ if ((k = getgroups(ngroups_max, gids)) < 0) {
+ free(gids);
+ return -errno;
+ }
+ } else
+ k = 0;
+
+ STRV_FOREACH(i, context->supplementary_groups) {
+ const char *g;
+
+ if (k >= ngroups_max) {
+ free(gids);
+ return -E2BIG;
+ }
+
+ g = *i;
+ r = get_group_creds(&g, gids+k);
+ if (r < 0) {
+ free(gids);
+ return r;
+ }
+
+ k++;
+ }
+
+ if (setgroups(k, gids) < 0) {
+ free(gids);
+ return -errno;
+ }
+
+ free(gids);
+ }
+
+ return 0;
+}
+
+static int enforce_user(const ExecContext *context, uid_t uid) {
+ int r;
+ assert(context);
+
+ /* Sets (but doesn't lookup) the uid and make sure we keep the
+ * capabilities while doing so. */
+
+ if (context->capabilities) {
+ cap_t d;
+ static const cap_value_t bits[] = {
+ CAP_SETUID, /* Necessary so that we can run setresuid() below */
+ CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
+ };
+
+ /* First step: If we need to keep capabilities but
+ * drop privileges we need to make sure we keep our
+ * caps, whiel we drop privileges. */
+ if (uid != 0) {
+ int sb = context->secure_bits|SECURE_KEEP_CAPS;
+
+ if (prctl(PR_GET_SECUREBITS) != sb)
+ if (prctl(PR_SET_SECUREBITS, sb) < 0)
+ return -errno;
+ }
+
+ /* Second step: set the capabilities. This will reduce
+ * the capabilities to the minimum we need. */
+
+ if (!(d = cap_dup(context->capabilities)))
+ return -errno;
+
+ if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
+ cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
+ r = -errno;
+ cap_free(d);
+ return r;
+ }
+
+ if (cap_set_proc(d) < 0) {
+ r = -errno;
+ cap_free(d);
+ return r;
+ }
+
+ cap_free(d);
+ }
+
+ /* Third step: actually set the uids */
+ if (setresuid(uid, uid, uid) < 0)
+ return -errno;
+
+ /* At this point we should have all necessary capabilities but
+ are otherwise a normal user. However, the caps might got
+ corrupted due to the setresuid() so we need clean them up
+ later. This is done outside of this call. */
+
+ return 0;
+}
+
+#ifdef HAVE_PAM
+
+static int null_conv(
+ int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr) {
+
+ /* We don't support conversations */
+
+ return PAM_CONV_ERR;
+}
+
+static int setup_pam(
+ const char *name,
+ const char *user,
+ const char *tty,
+ char ***pam_env,
+ int fds[], unsigned n_fds) {
+
+ static const struct pam_conv conv = {
+ .conv = null_conv,
+ .appdata_ptr = NULL
+ };
+
+ pam_handle_t *handle = NULL;
+ sigset_t ss, old_ss;
+ int pam_code = PAM_SUCCESS;
+ int err;
+ char **e = NULL;
+ bool close_session = false;
+ pid_t pam_pid = 0, parent_pid;
+
+ assert(name);
+ assert(user);
+ assert(pam_env);
+
+ /* We set up PAM in the parent process, then fork. The child
+ * will then stay around until killed via PR_GET_PDEATHSIG or
+ * systemd via the cgroup logic. It will then remove the PAM
+ * session again. The parent process will exec() the actual
+ * daemon. We do things this way to ensure that the main PID
+ * of the daemon is the one we initially fork()ed. */
+
+ if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
+ handle = NULL;
+ goto fail;
+ }
+
+ if (tty)
+ if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ close_session = true;
+
+ if ((!(e = pam_getenvlist(handle)))) {
+ pam_code = PAM_BUF_ERR;
+ goto fail;
+ }
+
+ /* Block SIGTERM, so that we know that it won't get lost in
+ * the child */
+ if (sigemptyset(&ss) < 0 ||
+ sigaddset(&ss, SIGTERM) < 0 ||
+ sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
+ goto fail;
+
+ parent_pid = getpid();
+
+ if ((pam_pid = fork()) < 0)
+ goto fail;
+
+ if (pam_pid == 0) {
+ int sig;
+ int r = EXIT_PAM;
+
+ /* The child's job is to reset the PAM session on
+ * termination */
+
+ /* This string must fit in 10 chars (i.e. the length
+ * of "/sbin/init"), to look pretty in /bin/ps */
+ rename_process("(sd-pam)");
+
+ /* Make sure we don't keep open the passed fds in this
+ child. We assume that otherwise only those fds are
+ open here that have been opened by PAM. */
+ close_many(fds, n_fds);
+
+ /* Wait until our parent died. This will most likely
+ * not work since the kernel does not allow
+ * unprivileged parents kill their privileged children
+ * this way. We rely on the control groups kill logic
+ * to do the rest for us. */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ goto child_finish;
+
+ /* Check if our parent process might already have
+ * died? */
+ if (getppid() == parent_pid) {
+ for (;;) {
+ if (sigwait(&ss, &sig) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ goto child_finish;
+ }
+
+ assert(sig == SIGTERM);
+ break;
+ }
+ }
+
+ /* If our parent died we'll end the session */
+ if (getppid() != parent_pid)
+ if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
+ goto child_finish;
+
+ r = 0;
+
+ child_finish:
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ _exit(r);
+ }
+
+ /* If the child was forked off successfully it will do all the
+ * cleanups, so forget about the handle here. */
+ handle = NULL;
+
+ /* Unblock SIGTERM again in the parent */
+ if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
+ goto fail;
+
+ /* We close the log explicitly here, since the PAM modules
+ * might have opened it, but we don't want this fd around. */
+ closelog();
+
+ *pam_env = e;
+ e = NULL;
+
+ return 0;
+
+fail:
+ if (pam_code != PAM_SUCCESS)
+ err = -EPERM; /* PAM errors do not map to errno */
+ else
+ err = -errno;
+
+ if (handle) {
+ if (close_session)
+ pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ }
+
+ strv_free(e);
+
+ closelog();
+
+ if (pam_pid > 1) {
+ kill(pam_pid, SIGTERM);
+ kill(pam_pid, SIGCONT);
+ }
+
+ return err;
+}
+#endif
+
+static int do_capability_bounding_set_drop(uint64_t drop) {
+ unsigned long i;
+ cap_t old_cap = NULL, new_cap = NULL;
+ cap_flag_value_t fv;
+ int r;
+
+ /* If we are run as PID 1 we will lack CAP_SETPCAP by default
+ * in the effective set (yes, the kernel drops that when
+ * executing init!), so get it back temporarily so that we can
+ * call PR_CAPBSET_DROP. */
+
+ old_cap = cap_get_proc();
+ if (!old_cap)
+ return -errno;
+
+ if (cap_get_flag(old_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (fv != CAP_SET) {
+ static const cap_value_t v = CAP_SETPCAP;
+
+ new_cap = cap_dup(old_cap);
+ if (!new_cap) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_flag(new_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (cap_set_proc(new_cap) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ for (i = 0; i <= cap_last_cap(); i++)
+ if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
+ if (prctl(PR_CAPBSET_DROP, i) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (new_cap)
+ cap_free(new_cap);
+
+ if (old_cap) {
+ cap_set_proc(old_cap);
+ cap_free(old_cap);
+ }
+
+ return r;
+}
+
+static void rename_process_from_path(const char *path) {
+ char process_name[11];
+ const char *p;
+ size_t l;
+
+ /* This resulting string must fit in 10 chars (i.e. the length
+ * of "/sbin/init") to look pretty in /bin/ps */
+
+ p = file_name_from_path(path);
+ if (isempty(p)) {
+ rename_process("(...)");
+ return;
+ }
+
+ l = strlen(p);
+ if (l > 8) {
+ /* The end of the process name is usually more
+ * interesting, since the first bit might just be
+ * "systemd-" */
+ p = p + l - 8;
+ l = 8;
+ }
+
+ process_name[0] = '(';
+ memcpy(process_name+1, p, l);
+ process_name[1+l] = ')';
+ process_name[1+l+1] = 0;
+
+ rename_process(process_name);
+}
+
+int exec_spawn(ExecCommand *command,
+ char **argv,
+ const ExecContext *context,
+ int fds[], unsigned n_fds,
+ char **environment,
+ bool apply_permissions,
+ bool apply_chroot,
+ bool apply_tty_stdin,
+ bool confirm_spawn,
+ CGroupBonding *cgroup_bondings,
+ CGroupAttribute *cgroup_attributes,
+ pid_t *ret) {
+
+ pid_t pid;
+ int r;
+ char *line;
+ int socket_fd;
+ char **files_env = NULL;
+
+ assert(command);
+ assert(context);
+ assert(ret);
+ assert(fds || n_fds <= 0);
+
+ if (context->std_input == EXEC_INPUT_SOCKET ||
+ context->std_output == EXEC_OUTPUT_SOCKET ||
+ context->std_error == EXEC_OUTPUT_SOCKET) {
+
+ if (n_fds != 1)
+ return -EINVAL;
+
+ socket_fd = fds[0];
+
+ fds = NULL;
+ n_fds = 0;
+ } else
+ socket_fd = -1;
+
+ if ((r = exec_context_load_environment(context, &files_env)) < 0) {
+ log_error("Failed to load environment files: %s", strerror(-r));
+ return r;
+ }
+
+ if (!argv)
+ argv = command->argv;
+
+ if (!(line = exec_command_line(argv))) {
+ r = -ENOMEM;
+ goto fail_parent;
+ }
+
+ log_debug("About to execute: %s", line);
+ free(line);
+
+ r = cgroup_bonding_realize_list(cgroup_bondings);
+ if (r < 0)
+ goto fail_parent;
+
+ cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
+
+ if ((pid = fork()) < 0) {
+ r = -errno;
+ goto fail_parent;
+ }
+
+ if (pid == 0) {
+ int i, err;
+ sigset_t ss;
+ const char *username = NULL, *home = NULL;
+ uid_t uid = (uid_t) -1;
+ gid_t gid = (gid_t) -1;
+ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+ unsigned n_env = 0;
+ int saved_stdout = -1, saved_stdin = -1;
+ bool keep_stdout = false, keep_stdin = false, set_access = false;
+
+ /* child */
+
+ rename_process_from_path(command->path);
+
+ /* We reset exactly these signals, since they are the
+ * only ones we set to SIG_IGN in the main daemon. All
+ * others we leave untouched because we set them to
+ * SIG_DFL or a valid handler initially, both of which
+ * will be demoted to SIG_DFL. */
+ default_signals(SIGNALS_CRASH_HANDLER,
+ SIGNALS_IGNORE, -1);
+
+ if (context->ignore_sigpipe)
+ ignore_signals(SIGPIPE, -1);
+
+ assert_se(sigemptyset(&ss) == 0);
+ if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
+ err = -errno;
+ r = EXIT_SIGNAL_MASK;
+ goto fail_child;
+ }
+
+ /* Close sockets very early to make sure we don't
+ * block init reexecution because it cannot bind its
+ * sockets */
+ log_forget_fds();
+ err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds,
+ socket_fd >= 0 ? 1 : n_fds);
+ if (err < 0) {
+ r = EXIT_FDS;
+ goto fail_child;
+ }
+
+ if (!context->same_pgrp)
+ if (setsid() < 0) {
+ err = -errno;
+ r = EXIT_SETSID;
+ goto fail_child;
+ }
+
+ if (context->tcpwrap_name) {
+ if (socket_fd >= 0)
+ if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) {
+ err = -EACCES;
+ r = EXIT_TCPWRAP;
+ goto fail_child;
+ }
+
+ for (i = 0; i < (int) n_fds; i++) {
+ if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) {
+ err = -EACCES;
+ r = EXIT_TCPWRAP;
+ goto fail_child;
+ }
+ }
+ }
+
+ exec_context_tty_reset(context);
+
+ /* We skip the confirmation step if we shall not apply the TTY */
+ if (confirm_spawn &&
+ (!is_terminal_input(context->std_input) || apply_tty_stdin)) {
+ char response;
+
+ /* Set up terminal for the question */
+ if ((r = setup_confirm_stdio(context,
+ &saved_stdin, &saved_stdout))) {
+ err = -errno;
+ goto fail_child;
+ }
+
+ /* Now ask the question. */
+ if (!(line = exec_command_line(argv))) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ r = ask(&response, "yns", "Execute %s? [Yes, No, Skip] ", line);
+ free(line);
+
+ if (r < 0 || response == 'n') {
+ err = -ECANCELED;
+ r = EXIT_CONFIRM;
+ goto fail_child;
+ } else if (response == 's') {
+ err = r = 0;
+ goto fail_child;
+ }
+
+ /* Release terminal for the question */
+ if ((r = restore_confirm_stdio(context,
+ &saved_stdin, &saved_stdout,
+ &keep_stdin, &keep_stdout))) {
+ err = -errno;
+ goto fail_child;
+ }
+ }
+
+ /* If a socket is connected to STDIN/STDOUT/STDERR, we
+ * must sure to drop O_NONBLOCK */
+ if (socket_fd >= 0)
+ fd_nonblock(socket_fd, false);
+
+ if (!keep_stdin) {
+ err = setup_input(context, socket_fd, apply_tty_stdin);
+ if (err < 0) {
+ r = EXIT_STDIN;
+ goto fail_child;
+ }
+ }
+
+ if (!keep_stdout) {
+ err = setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin);
+ if (err < 0) {
+ r = EXIT_STDOUT;
+ goto fail_child;
+ }
+ }
+
+ err = setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin);
+ if (err < 0) {
+ r = EXIT_STDERR;
+ goto fail_child;
+ }
+
+ if (cgroup_bondings) {
+ err = cgroup_bonding_install_list(cgroup_bondings, 0);
+ if (err < 0) {
+ r = EXIT_CGROUP;
+ goto fail_child;
+ }
+ }
+
+ if (context->oom_score_adjust_set) {
+ char t[16];
+
+ snprintf(t, sizeof(t), "%i", context->oom_score_adjust);
+ char_array_0(t);
+
+ if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) {
+ /* Compatibility with Linux <= 2.6.35 */
+
+ int adj;
+
+ adj = (context->oom_score_adjust * -OOM_DISABLE) / OOM_SCORE_ADJ_MAX;
+ adj = CLAMP(adj, OOM_DISABLE, OOM_ADJUST_MAX);
+
+ snprintf(t, sizeof(t), "%i", adj);
+ char_array_0(t);
+
+ if (write_one_line_file("/proc/self/oom_adj", t) < 0
+ && errno != EACCES) {
+ err = -errno;
+ r = EXIT_OOM_ADJUST;
+ goto fail_child;
+ }
+ }
+ }
+
+ if (context->nice_set)
+ if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
+ err = -errno;
+ r = EXIT_NICE;
+ goto fail_child;
+ }
+
+ if (context->cpu_sched_set) {
+ struct sched_param param;
+
+ zero(param);
+ param.sched_priority = context->cpu_sched_priority;
+
+ if (sched_setscheduler(0, context->cpu_sched_policy |
+ (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) {
+ err = -errno;
+ r = EXIT_SETSCHEDULER;
+ goto fail_child;
+ }
+ }
+
+ if (context->cpuset)
+ if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) {
+ err = -errno;
+ r = EXIT_CPUAFFINITY;
+ goto fail_child;
+ }
+
+ if (context->ioprio_set)
+ if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
+ err = -errno;
+ r = EXIT_IOPRIO;
+ goto fail_child;
+ }
+
+ if (context->timer_slack_nsec_set)
+ if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) {
+ err = -errno;
+ r = EXIT_TIMERSLACK;
+ goto fail_child;
+ }
+
+ if (context->utmp_id)
+ utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
+
+ if (context->user) {
+ username = context->user;
+ err = get_user_creds(&username, &uid, &gid, &home);
+ if (err < 0) {
+ r = EXIT_USER;
+ goto fail_child;
+ }
+
+ if (is_terminal_input(context->std_input)) {
+ err = chown_terminal(STDIN_FILENO, uid);
+ if (err < 0) {
+ r = EXIT_STDIN;
+ goto fail_child;
+ }
+ }
+
+ if (cgroup_bondings && context->control_group_modify) {
+ err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
+ if (err >= 0)
+ err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent);
+ if (err < 0) {
+ r = EXIT_CGROUP;
+ goto fail_child;
+ }
+
+ set_access = true;
+ }
+ }
+
+ if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) {
+ err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent);
+ if (err < 0) {
+ r = EXIT_CGROUP;
+ goto fail_child;
+ }
+ }
+
+ if (apply_permissions) {
+ err = enforce_groups(context, username, gid);
+ if (err < 0) {
+ r = EXIT_GROUP;
+ goto fail_child;
+ }
+ }
+
+ umask(context->umask);
+
+#ifdef HAVE_PAM
+ if (context->pam_name && username) {
+ err = setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds);
+ if (err < 0) {
+ r = EXIT_PAM;
+ goto fail_child;
+ }
+ }
+#endif
+ if (context->private_network) {
+ if (unshare(CLONE_NEWNET) < 0) {
+ err = -errno;
+ r = EXIT_NETWORK;
+ goto fail_child;
+ }
+
+ loopback_setup();
+ }
+
+ if (strv_length(context->read_write_dirs) > 0 ||
+ strv_length(context->read_only_dirs) > 0 ||
+ strv_length(context->inaccessible_dirs) > 0 ||
+ context->mount_flags != MS_SHARED ||
+ context->private_tmp) {
+ err = setup_namespace(context->read_write_dirs,
+ context->read_only_dirs,
+ context->inaccessible_dirs,
+ context->private_tmp,
+ context->mount_flags);
+ if (err < 0) {
+ r = EXIT_NAMESPACE;
+ goto fail_child;
+ }
+ }
+
+ if (apply_chroot) {
+ if (context->root_directory)
+ if (chroot(context->root_directory) < 0) {
+ err = -errno;
+ r = EXIT_CHROOT;
+ goto fail_child;
+ }
+
+ if (chdir(context->working_directory ? context->working_directory : "/") < 0) {
+ err = -errno;
+ r = EXIT_CHDIR;
+ goto fail_child;
+ }
+ } else {
+
+ char *d;
+
+ if (asprintf(&d, "%s/%s",
+ context->root_directory ? context->root_directory : "",
+ context->working_directory ? context->working_directory : "") < 0) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (chdir(d) < 0) {
+ err = -errno;
+ free(d);
+ r = EXIT_CHDIR;
+ goto fail_child;
+ }
+
+ free(d);
+ }
+
+ /* We repeat the fd closing here, to make sure that
+ * nothing is leaked from the PAM modules */
+ err = close_all_fds(fds, n_fds);
+ if (err >= 0)
+ err = shift_fds(fds, n_fds);
+ if (err >= 0)
+ err = flags_fds(fds, n_fds, context->non_blocking);
+ if (err < 0) {
+ r = EXIT_FDS;
+ goto fail_child;
+ }
+
+ if (apply_permissions) {
+
+ for (i = 0; i < RLIMIT_NLIMITS; i++) {
+ if (!context->rlimit[i])
+ continue;
+
+ if (setrlimit(i, context->rlimit[i]) < 0) {
+ err = -errno;
+ r = EXIT_LIMITS;
+ goto fail_child;
+ }
+ }
+
+ if (context->capability_bounding_set_drop) {
+ err = do_capability_bounding_set_drop(context->capability_bounding_set_drop);
+ if (err < 0) {
+ r = EXIT_CAPABILITIES;
+ goto fail_child;
+ }
+ }
+
+ if (context->user) {
+ err = enforce_user(context, uid);
+ if (err < 0) {
+ r = EXIT_USER;
+ goto fail_child;
+ }
+ }
+
+ /* PR_GET_SECUREBITS is not privileged, while
+ * PR_SET_SECUREBITS is. So to suppress
+ * potential EPERMs we'll try not to call
+ * PR_SET_SECUREBITS unless necessary. */
+ if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
+ if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+ err = -errno;
+ r = EXIT_SECUREBITS;
+ goto fail_child;
+ }
+
+ if (context->capabilities)
+ if (cap_set_proc(context->capabilities) < 0) {
+ err = -errno;
+ r = EXIT_CAPABILITIES;
+ goto fail_child;
+ }
+ }
+
+ if (!(our_env = new0(char*, 7))) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (n_fds > 0)
+ if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 ||
+ asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (home)
+ if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (username)
+ if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 ||
+ asprintf(our_env + n_env++, "USER=%s", username) < 0) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (is_terminal_input(context->std_input) ||
+ context->std_output == EXEC_OUTPUT_TTY ||
+ context->std_error == EXEC_OUTPUT_TTY)
+ if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ assert(n_env <= 7);
+
+ if (!(final_env = strv_env_merge(
+ 5,
+ environment,
+ our_env,
+ context->environment,
+ files_env,
+ pam_env,
+ NULL))) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ if (!(final_argv = replace_env_argv(argv, final_env))) {
+ err = -ENOMEM;
+ r = EXIT_MEMORY;
+ goto fail_child;
+ }
+
+ final_env = strv_env_clean(final_env);
+
+ execve(command->path, final_argv, final_env);
+ err = -errno;
+ r = EXIT_EXEC;
+
+ fail_child:
+ if (r != 0) {
+ log_open();
+ log_warning("Failed at step %s spawning %s: %s",
+ exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
+ command->path, strerror(-err));
+ }
+
+ strv_free(our_env);
+ strv_free(final_env);
+ strv_free(pam_env);
+ strv_free(files_env);
+ strv_free(final_argv);
+
+ if (saved_stdin >= 0)
+ close_nointr_nofail(saved_stdin);
+
+ if (saved_stdout >= 0)
+ close_nointr_nofail(saved_stdout);
+
+ _exit(r);
+ }
+
+ strv_free(files_env);
+
+ /* We add the new process to the cgroup both in the child (so
+ * that we can be sure that no user code is ever executed
+ * outside of the cgroup) and in the parent (so that we can be
+ * sure that when we kill the cgroup the process will be
+ * killed too). */
+ if (cgroup_bondings)
+ cgroup_bonding_install_list(cgroup_bondings, pid);
+
+ log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
+
+ exec_status_start(&command->exec_status, pid);
+
+ *ret = pid;
+ return 0;
+
+fail_parent:
+ strv_free(files_env);
+
+ return r;
+}
+
+void exec_context_init(ExecContext *c) {
+ assert(c);
+
+ c->umask = 0022;
+ c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
+ c->cpu_sched_policy = SCHED_OTHER;
+ c->syslog_priority = LOG_DAEMON|LOG_INFO;
+ c->syslog_level_prefix = true;
+ c->mount_flags = MS_SHARED;
+ c->kill_signal = SIGTERM;
+ c->send_sigkill = true;
+ c->control_group_persistent = -1;
+ c->ignore_sigpipe = true;
+}
+
+void exec_context_done(ExecContext *c) {
+ unsigned l;
+
+ assert(c);
+
+ strv_free(c->environment);
+ c->environment = NULL;
+
+ strv_free(c->environment_files);
+ c->environment_files = NULL;
+
+ for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
+ free(c->rlimit[l]);
+ c->rlimit[l] = NULL;
+ }
+
+ free(c->working_directory);
+ c->working_directory = NULL;
+ free(c->root_directory);
+ c->root_directory = NULL;
+
+ free(c->tty_path);
+ c->tty_path = NULL;
+
+ free(c->tcpwrap_name);
+ c->tcpwrap_name = NULL;
+
+ free(c->syslog_identifier);
+ c->syslog_identifier = NULL;
+
+ free(c->user);
+ c->user = NULL;
+
+ free(c->group);
+ c->group = NULL;
+
+ strv_free(c->supplementary_groups);
+ c->supplementary_groups = NULL;
+
+ free(c->pam_name);
+ c->pam_name = NULL;
+
+ if (c->capabilities) {
+ cap_free(c->capabilities);
+ c->capabilities = NULL;
+ }
+
+ strv_free(c->read_only_dirs);
+ c->read_only_dirs = NULL;
+
+ strv_free(c->read_write_dirs);
+ c->read_write_dirs = NULL;
+
+ strv_free(c->inaccessible_dirs);
+ c->inaccessible_dirs = NULL;
+
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
+
+ free(c->utmp_id);
+ c->utmp_id = NULL;
+}
+
+void exec_command_done(ExecCommand *c) {
+ assert(c);
+
+ free(c->path);
+ c->path = NULL;
+
+ strv_free(c->argv);
+ c->argv = NULL;
+}
+
+void exec_command_done_array(ExecCommand *c, unsigned n) {
+ unsigned i;
+
+ for (i = 0; i < n; i++)
+ exec_command_done(c+i);
+}
+
+void exec_command_free_list(ExecCommand *c) {
+ ExecCommand *i;
+
+ while ((i = c)) {
+ LIST_REMOVE(ExecCommand, command, c, i);
+ exec_command_done(i);
+ free(i);
+ }
+}
+
+void exec_command_free_array(ExecCommand **c, unsigned n) {
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ exec_command_free_list(c[i]);
+ c[i] = NULL;
+ }
+}
+
+int exec_context_load_environment(const ExecContext *c, char ***l) {
+ char **i, **r = NULL;
+
+ assert(c);
+ assert(l);
+
+ STRV_FOREACH(i, c->environment_files) {
+ char *fn;
+ int k;
+ bool ignore = false;
+ char **p;
+
+ fn = *i;
+
+ if (fn[0] == '-') {
+ ignore = true;
+ fn ++;
+ }
+
+ if (!path_is_absolute(fn)) {
+
+ if (ignore)
+ continue;
+
+ strv_free(r);
+ return -EINVAL;
+ }
+
+ if ((k = load_env_file(fn, &p)) < 0) {
+
+ if (ignore)
+ continue;
+
+ strv_free(r);
+ return k;
+ }
+
+ if (r == NULL)
+ r = p;
+ else {
+ char **m;
+
+ m = strv_env_merge(2, r, p);
+ strv_free(r);
+ strv_free(p);
+
+ if (!m)
+ return -ENOMEM;
+
+ r = m;
+ }
+ }
+
+ *l = r;
+
+ return 0;
+}
+
+static void strv_fprintf(FILE *f, char **l) {
+ char **g;
+
+ assert(f);
+
+ STRV_FOREACH(g, l)
+ fprintf(f, " %s", *g);
+}
+
+void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
+ char ** e;
+ unsigned i;
+
+ assert(c);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ fprintf(f,
+ "%sUMask: %04o\n"
+ "%sWorkingDirectory: %s\n"
+ "%sRootDirectory: %s\n"
+ "%sNonBlocking: %s\n"
+ "%sPrivateTmp: %s\n"
+ "%sControlGroupModify: %s\n"
+ "%sControlGroupPersistent: %s\n"
+ "%sPrivateNetwork: %s\n",
+ prefix, c->umask,
+ prefix, c->working_directory ? c->working_directory : "/",
+ prefix, c->root_directory ? c->root_directory : "/",
+ prefix, yes_no(c->non_blocking),
+ prefix, yes_no(c->private_tmp),
+ prefix, yes_no(c->control_group_modify),
+ prefix, yes_no(c->control_group_persistent),
+ prefix, yes_no(c->private_network));
+
+ STRV_FOREACH(e, c->environment)
+ fprintf(f, "%sEnvironment: %s\n", prefix, *e);
+
+ STRV_FOREACH(e, c->environment_files)
+ fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
+
+ if (c->tcpwrap_name)
+ fprintf(f,
+ "%sTCPWrapName: %s\n",
+ prefix, c->tcpwrap_name);
+
+ if (c->nice_set)
+ fprintf(f,
+ "%sNice: %i\n",
+ prefix, c->nice);
+
+ if (c->oom_score_adjust_set)
+ fprintf(f,
+ "%sOOMScoreAdjust: %i\n",
+ prefix, c->oom_score_adjust);
+
+ for (i = 0; i < RLIM_NLIMITS; i++)
+ if (c->rlimit[i])
+ fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
+
+ if (c->ioprio_set)
+ fprintf(f,
+ "%sIOSchedulingClass: %s\n"
+ "%sIOPriority: %i\n",
+ prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
+ prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
+
+ if (c->cpu_sched_set)
+ fprintf(f,
+ "%sCPUSchedulingPolicy: %s\n"
+ "%sCPUSchedulingPriority: %i\n"
+ "%sCPUSchedulingResetOnFork: %s\n",
+ prefix, sched_policy_to_string(c->cpu_sched_policy),
+ prefix, c->cpu_sched_priority,
+ prefix, yes_no(c->cpu_sched_reset_on_fork));
+
+ if (c->cpuset) {
+ fprintf(f, "%sCPUAffinity:", prefix);
+ for (i = 0; i < c->cpuset_ncpus; i++)
+ if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
+ fprintf(f, " %i", i);
+ fputs("\n", f);
+ }
+
+ if (c->timer_slack_nsec_set)
+ fprintf(f, "%sTimerSlackNSec: %lu\n", prefix, c->timer_slack_nsec);
+
+ fprintf(f,
+ "%sStandardInput: %s\n"
+ "%sStandardOutput: %s\n"
+ "%sStandardError: %s\n",
+ prefix, exec_input_to_string(c->std_input),
+ prefix, exec_output_to_string(c->std_output),
+ prefix, exec_output_to_string(c->std_error));
+
+ if (c->tty_path)
+ fprintf(f,
+ "%sTTYPath: %s\n"
+ "%sTTYReset: %s\n"
+ "%sTTYVHangup: %s\n"
+ "%sTTYVTDisallocate: %s\n",
+ prefix, c->tty_path,
+ prefix, yes_no(c->tty_reset),
+ prefix, yes_no(c->tty_vhangup),
+ prefix, yes_no(c->tty_vt_disallocate));
+
+ if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL ||
+ c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
+ c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL ||
+ c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE)
+ fprintf(f,
+ "%sSyslogFacility: %s\n"
+ "%sSyslogLevel: %s\n",
+ prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3),
+ prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
+
+ if (c->capabilities) {
+ char *t;
+ if ((t = cap_to_text(c->capabilities, NULL))) {
+ fprintf(f, "%sCapabilities: %s\n",
+ prefix, t);
+ cap_free(t);
+ }
+ }
+
+ if (c->secure_bits)
+ fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
+ prefix,
+ (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
+ (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
+ (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
+ (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
+ (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
+ (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
+
+ if (c->capability_bounding_set_drop) {
+ unsigned long l;
+ fprintf(f, "%sCapabilityBoundingSet:", prefix);
+
+ for (l = 0; l <= cap_last_cap(); l++)
+ if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) {
+ char *t;
+
+ if ((t = cap_to_name(l))) {
+ fprintf(f, " %s", t);
+ cap_free(t);
+ }
+ }
+
+ fputs("\n", f);
+ }
+
+ if (c->user)
+ fprintf(f, "%sUser: %s\n", prefix, c->user);
+ if (c->group)
+ fprintf(f, "%sGroup: %s\n", prefix, c->group);
+
+ if (strv_length(c->supplementary_groups) > 0) {
+ fprintf(f, "%sSupplementaryGroups:", prefix);
+ strv_fprintf(f, c->supplementary_groups);
+ fputs("\n", f);
+ }
+
+ if (c->pam_name)
+ fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
+
+ if (strv_length(c->read_write_dirs) > 0) {
+ fprintf(f, "%sReadWriteDirs:", prefix);
+ strv_fprintf(f, c->read_write_dirs);
+ fputs("\n", f);
+ }
+
+ if (strv_length(c->read_only_dirs) > 0) {
+ fprintf(f, "%sReadOnlyDirs:", prefix);
+ strv_fprintf(f, c->read_only_dirs);
+ fputs("\n", f);
+ }
+
+ if (strv_length(c->inaccessible_dirs) > 0) {
+ fprintf(f, "%sInaccessibleDirs:", prefix);
+ strv_fprintf(f, c->inaccessible_dirs);
+ fputs("\n", f);
+ }
+
+ fprintf(f,
+ "%sKillMode: %s\n"
+ "%sKillSignal: SIG%s\n"
+ "%sSendSIGKILL: %s\n"
+ "%sIgnoreSIGPIPE: %s\n",
+ prefix, kill_mode_to_string(c->kill_mode),
+ prefix, signal_to_string(c->kill_signal),
+ prefix, yes_no(c->send_sigkill),
+ prefix, yes_no(c->ignore_sigpipe));
+
+ if (c->utmp_id)
+ fprintf(f,
+ "%sUtmpIdentifier: %s\n",
+ prefix, c->utmp_id);
+}
+
+void exec_status_start(ExecStatus *s, pid_t pid) {
+ assert(s);
+
+ zero(*s);
+ s->pid = pid;
+ dual_timestamp_get(&s->start_timestamp);
+}
+
+void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) {
+ assert(s);
+
+ if (s->pid && s->pid != pid)
+ zero(*s);
+
+ s->pid = pid;
+ dual_timestamp_get(&s->exit_timestamp);
+
+ s->code = code;
+ s->status = status;
+
+ if (context) {
+ if (context->utmp_id)
+ utmp_put_dead_process(context->utmp_id, pid, code, status);
+
+ exec_context_tty_reset(context);
+ }
+}
+
+void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+
+ assert(s);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ if (s->pid <= 0)
+ return;
+
+ fprintf(f,
+ "%sPID: %lu\n",
+ prefix, (unsigned long) s->pid);
+
+ if (s->start_timestamp.realtime > 0)
+ fprintf(f,
+ "%sStart Timestamp: %s\n",
+ prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
+
+ if (s->exit_timestamp.realtime > 0)
+ fprintf(f,
+ "%sExit Timestamp: %s\n"
+ "%sExit Code: %s\n"
+ "%sExit Status: %i\n",
+ prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime),
+ prefix, sigchld_code_to_string(s->code),
+ prefix, s->status);
+}
+
+char *exec_command_line(char **argv) {
+ size_t k;
+ char *n, *p, **a;
+ bool first = true;
+
+ assert(argv);
+
+ k = 1;
+ STRV_FOREACH(a, argv)
+ k += strlen(*a)+3;
+
+ if (!(n = new(char, k)))
+ return NULL;
+
+ p = n;
+ STRV_FOREACH(a, argv) {
+
+ if (!first)
+ *(p++) = ' ';
+ else
+ first = false;
+
+ if (strpbrk(*a, WHITESPACE)) {
+ *(p++) = '\'';
+ p = stpcpy(p, *a);
+ *(p++) = '\'';
+ } else
+ p = stpcpy(p, *a);
+
+ }
+
+ *p = 0;
+
+ /* FIXME: this doesn't really handle arguments that have
+ * spaces and ticks in them */
+
+ return n;
+}
+
+void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
+ char *p2;
+ const char *prefix2;
+
+ char *cmd;
+
+ assert(c);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+ p2 = strappend(prefix, "\t");
+ prefix2 = p2 ? p2 : prefix;
+
+ cmd = exec_command_line(c->argv);
+
+ fprintf(f,
+ "%sCommand Line: %s\n",
+ prefix, cmd ? cmd : strerror(ENOMEM));
+
+ free(cmd);
+
+ exec_status_dump(&c->exec_status, f, prefix2);
+
+ free(p2);
+}
+
+void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ LIST_FOREACH(command, c, c)
+ exec_command_dump(c, f, prefix);
+}
+
+void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
+ ExecCommand *end;
+
+ assert(l);
+ assert(e);
+
+ if (*l) {
+ /* It's kind of important, that we keep the order here */
+ LIST_FIND_TAIL(ExecCommand, command, *l, end);
+ LIST_INSERT_AFTER(ExecCommand, command, *l, end, e);
+ } else
+ *l = e;
+}
+
+int exec_command_set(ExecCommand *c, const char *path, ...) {
+ va_list ap;
+ char **l, *p;
+
+ assert(c);
+ assert(path);
+
+ va_start(ap, path);
+ l = strv_new_ap(path, ap);
+ va_end(ap);
+
+ if (!l)
+ return -ENOMEM;
+
+ if (!(p = strdup(path))) {
+ strv_free(l);
+ return -ENOMEM;
+ }
+
+ free(c->path);
+ c->path = p;
+
+ strv_free(c->argv);
+ c->argv = l;
+
+ return 0;
+}
+
+static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
+ [EXEC_INPUT_NULL] = "null",
+ [EXEC_INPUT_TTY] = "tty",
+ [EXEC_INPUT_TTY_FORCE] = "tty-force",
+ [EXEC_INPUT_TTY_FAIL] = "tty-fail",
+ [EXEC_INPUT_SOCKET] = "socket"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
+
+static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
+ [EXEC_OUTPUT_INHERIT] = "inherit",
+ [EXEC_OUTPUT_NULL] = "null",
+ [EXEC_OUTPUT_TTY] = "tty",
+ [EXEC_OUTPUT_SYSLOG] = "syslog",
+ [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console",
+ [EXEC_OUTPUT_KMSG] = "kmsg",
+ [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
+ [EXEC_OUTPUT_JOURNAL] = "journal",
+ [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
+ [EXEC_OUTPUT_SOCKET] = "socket"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
+
+static const char* const kill_mode_table[_KILL_MODE_MAX] = {
+ [KILL_CONTROL_GROUP] = "control-group",
+ [KILL_PROCESS] = "process",
+ [KILL_NONE] = "none"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
+
+static const char* const kill_who_table[_KILL_WHO_MAX] = {
+ [KILL_MAIN] = "main",
+ [KILL_CONTROL] = "control",
+ [KILL_ALL] = "all"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
diff --git a/src/core/execute.h b/src/core/execute.h
new file mode 100644
index 0000000..0d7e7dd
--- /dev/null
+++ b/src/core/execute.h
@@ -0,0 +1,233 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooexecutehfoo
+#define fooexecutehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct ExecStatus ExecStatus;
+typedef struct ExecCommand ExecCommand;
+typedef struct ExecContext ExecContext;
+
+#include <linux/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/capability.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct CGroupBonding;
+struct CGroupAttribute;
+
+#include "list.h"
+#include "util.h"
+
+typedef enum KillMode {
+ KILL_CONTROL_GROUP = 0,
+ KILL_PROCESS,
+ KILL_NONE,
+ _KILL_MODE_MAX,
+ _KILL_MODE_INVALID = -1
+} KillMode;
+
+typedef enum KillWho {
+ KILL_MAIN,
+ KILL_CONTROL,
+ KILL_ALL,
+ _KILL_WHO_MAX,
+ _KILL_WHO_INVALID = -1
+} KillWho;
+
+typedef enum ExecInput {
+ EXEC_INPUT_NULL,
+ EXEC_INPUT_TTY,
+ EXEC_INPUT_TTY_FORCE,
+ EXEC_INPUT_TTY_FAIL,
+ EXEC_INPUT_SOCKET,
+ _EXEC_INPUT_MAX,
+ _EXEC_INPUT_INVALID = -1
+} ExecInput;
+
+typedef enum ExecOutput {
+ EXEC_OUTPUT_INHERIT,
+ EXEC_OUTPUT_NULL,
+ EXEC_OUTPUT_TTY,
+ EXEC_OUTPUT_SYSLOG,
+ EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG,
+ EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_JOURNAL,
+ EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
+ EXEC_OUTPUT_SOCKET,
+ _EXEC_OUTPUT_MAX,
+ _EXEC_OUTPUT_INVALID = -1
+} ExecOutput;
+
+struct ExecStatus {
+ dual_timestamp start_timestamp;
+ dual_timestamp exit_timestamp;
+ pid_t pid;
+ int code; /* as in siginfo_t::si_code */
+ int status; /* as in sigingo_t::si_status */
+};
+
+struct ExecCommand {
+ char *path;
+ char **argv;
+ ExecStatus exec_status;
+ LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
+ bool ignore;
+};
+
+struct ExecContext {
+ char **environment;
+ char **environment_files;
+
+ struct rlimit *rlimit[RLIMIT_NLIMITS];
+ char *working_directory, *root_directory;
+
+ mode_t umask;
+ int oom_score_adjust;
+ int nice;
+ int ioprio;
+ int cpu_sched_policy;
+ int cpu_sched_priority;
+
+ cpu_set_t *cpuset;
+ unsigned cpuset_ncpus;
+
+ ExecInput std_input;
+ ExecOutput std_output;
+ ExecOutput std_error;
+
+ unsigned long timer_slack_nsec;
+
+ char *tcpwrap_name;
+
+ char *tty_path;
+
+ bool tty_reset;
+ bool tty_vhangup;
+ bool tty_vt_disallocate;
+
+ bool ignore_sigpipe;
+
+ /* Since resolving these names might might involve socket
+ * connections and we don't want to deadlock ourselves these
+ * names are resolved on execution only and in the child
+ * process. */
+ char *user;
+ char *group;
+ char **supplementary_groups;
+
+ char *pam_name;
+
+ char *utmp_id;
+
+ char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
+ unsigned long mount_flags;
+
+ uint64_t capability_bounding_set_drop;
+
+ /* Not relevant for spawning processes, just for killing */
+ KillMode kill_mode;
+ int kill_signal;
+ bool send_sigkill;
+
+ cap_t capabilities;
+ int secure_bits;
+
+ int syslog_priority;
+ char *syslog_identifier;
+ bool syslog_level_prefix;
+
+ bool cpu_sched_reset_on_fork;
+ bool non_blocking;
+ bool private_tmp;
+ bool private_network;
+
+ bool control_group_modify;
+ int control_group_persistent;
+
+ /* This is not exposed to the user but available
+ * internally. We need it to make sure that whenever we spawn
+ * /bin/mount it is run in the same process group as us so
+ * that the autofs logic detects that it belongs to us and we
+ * don't enter a trigger loop. */
+ bool same_pgrp;
+
+ bool oom_score_adjust_set:1;
+ bool nice_set:1;
+ bool ioprio_set:1;
+ bool cpu_sched_set:1;
+ bool timer_slack_nsec_set:1;
+};
+
+int exec_spawn(ExecCommand *command,
+ char **argv,
+ const ExecContext *context,
+ int fds[], unsigned n_fds,
+ char **environment,
+ bool apply_permissions,
+ bool apply_chroot,
+ bool apply_tty_stdin,
+ bool confirm_spawn,
+ struct CGroupBonding *cgroup_bondings,
+ struct CGroupAttribute *cgroup_attributes,
+ pid_t *ret);
+
+void exec_command_done(ExecCommand *c);
+void exec_command_done_array(ExecCommand *c, unsigned n);
+
+void exec_command_free_list(ExecCommand *c);
+void exec_command_free_array(ExecCommand **c, unsigned n);
+
+char *exec_command_line(char **argv);
+
+void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix);
+void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
+void exec_command_append_list(ExecCommand **l, ExecCommand *e);
+int exec_command_set(ExecCommand *c, const char *path, ...);
+
+void exec_context_init(ExecContext *c);
+void exec_context_done(ExecContext *c);
+void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
+void exec_context_tty_reset(const ExecContext *context);
+
+int exec_context_load_environment(const ExecContext *c, char ***l);
+
+void exec_status_start(ExecStatus *s, pid_t pid);
+void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
+void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
+
+const char* exec_output_to_string(ExecOutput i);
+ExecOutput exec_output_from_string(const char *s);
+
+const char* exec_input_to_string(ExecInput i);
+ExecInput exec_input_from_string(const char *s);
+
+const char *kill_mode_to_string(KillMode k);
+KillMode kill_mode_from_string(const char *s);
+
+const char *kill_who_to_string(KillWho k);
+KillWho kill_who_from_string(const char *s);
+
+#endif
diff --git a/src/core/fdset.c b/src/core/fdset.c
new file mode 100644
index 0000000..e67fe6f
--- /dev/null
+++ b/src/core/fdset.c
@@ -0,0 +1,167 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "set.h"
+#include "util.h"
+#include "macro.h"
+#include "fdset.h"
+
+#define MAKE_SET(s) ((Set*) s)
+#define MAKE_FDSET(s) ((FDSet*) s)
+
+/* Make sure we can distuingish fd 0 and NULL */
+#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
+#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
+
+FDSet *fdset_new(void) {
+ return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func));
+}
+
+void fdset_free(FDSet *s) {
+ void *p;
+
+ while ((p = set_steal_first(MAKE_SET(s)))) {
+ /* Valgrind's fd might have ended up in this set here,
+ * due to fdset_new_fill(). We'll ignore all failures
+ * here, so that the EBADFD that valgrind will return
+ * us on close() doesn't influence us */
+
+ /* When reloading duplicates of the private bus
+ * connection fds and suchlike are closed here, which
+ * has no effect at all, since they are only
+ * duplicates. So don't be surprised about these log
+ * messages. */
+
+ log_debug("Closing left-over fd %i", PTR_TO_FD(p));
+ close_nointr(PTR_TO_FD(p));
+ }
+
+ set_free(MAKE_SET(s));
+}
+
+int fdset_put(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return set_put(MAKE_SET(s), FD_TO_PTR(fd));
+}
+
+int fdset_put_dup(FDSet *s, int fd) {
+ int copy, r;
+
+ assert(s);
+ assert(fd >= 0);
+
+ if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0)
+ return -errno;
+
+ if ((r = fdset_put(s, copy)) < 0) {
+ close_nointr_nofail(copy);
+ return r;
+ }
+
+ return copy;
+}
+
+bool fdset_contains(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
+}
+
+int fdset_remove(FDSet *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
+}
+
+int fdset_new_fill(FDSet **_s) {
+ DIR *d;
+ struct dirent *de;
+ int r = 0;
+ FDSet *s;
+
+ assert(_s);
+
+ /* Creates an fdsets and fills in all currently open file
+ * descriptors. */
+
+ if (!(d = opendir("/proc/self/fd")))
+ return -errno;
+
+ if (!(s = fdset_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ while ((de = readdir(d))) {
+ int fd = -1;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ if ((r = safe_atoi(de->d_name, &fd)) < 0)
+ goto finish;
+
+ if (fd < 3)
+ continue;
+
+ if (fd == dirfd(d))
+ continue;
+
+ if ((r = fdset_put(s, fd)) < 0)
+ goto finish;
+ }
+
+ r = 0;
+ *_s = s;
+ s = NULL;
+
+finish:
+ closedir(d);
+
+ /* We won't close the fds here! */
+ if (s)
+ set_free(MAKE_SET(s));
+
+ return r;
+}
+
+int fdset_cloexec(FDSet *fds, bool b) {
+ Iterator i;
+ void *p;
+ int r;
+
+ assert(fds);
+
+ SET_FOREACH(p, MAKE_SET(fds), i)
+ if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
+ return r;
+
+ return 0;
+}
diff --git a/src/core/fdset.h b/src/core/fdset.h
new file mode 100644
index 0000000..044a9e6
--- /dev/null
+++ b/src/core/fdset.h
@@ -0,0 +1,40 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foofdsethfoo
+#define foofdsethfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct FDSet FDSet;
+
+FDSet* fdset_new(void);
+void fdset_free(FDSet *s);
+
+int fdset_put(FDSet *s, int fd);
+int fdset_put_dup(FDSet *s, int fd);
+
+bool fdset_contains(FDSet *s, int fd);
+int fdset_remove(FDSet *s, int fd);
+
+int fdset_new_fill(FDSet **_s);
+
+int fdset_cloexec(FDSet *fds, bool b);
+
+#endif
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
new file mode 100644
index 0000000..03e43dc
--- /dev/null
+++ b/src/core/ima-setup.c
@@ -0,0 +1,115 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
+ TORSEC group -- http://security.polito.it
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "ima-setup.h"
+#include "mount-setup.h"
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+#include "label.h"
+
+#define IMA_SECFS_DIR "/sys/kernel/security/ima"
+#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy"
+#define IMA_POLICY_PATH "/etc/ima/ima-policy"
+
+int ima_setup(void) {
+
+#ifdef HAVE_IMA
+ struct stat st;
+ ssize_t policy_size = 0, written = 0;
+ char *policy;
+ int policyfd = -1, imafd = -1;
+ int result = 0;
+
+#ifndef HAVE_SELINUX
+ /* Mount the securityfs filesystem */
+ mount_setup_early();
+#endif
+
+ if (stat(IMA_POLICY_PATH, &st) < 0)
+ return 0;
+
+ policy_size = st.st_size;
+ if (stat(IMA_SECFS_DIR, &st) < 0) {
+ log_debug("IMA support is disabled in the kernel, ignoring.");
+ return 0;
+ }
+
+ if (stat(IMA_SECFS_POLICY, &st) < 0) {
+ log_error("Another IMA custom policy has already been loaded, "
+ "ignoring.");
+ return 0;
+ }
+
+ policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC);
+ if (policyfd < 0) {
+ log_error("Failed to open the IMA custom policy file %s (%m), "
+ "ignoring.", IMA_POLICY_PATH);
+ return 0;
+ }
+
+ imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC);
+ if (imafd < 0) {
+ log_error("Failed to open the IMA kernel interface %s (%m), "
+ "ignoring.", IMA_SECFS_POLICY);
+ goto out;
+ }
+
+ policy = mmap(NULL, policy_size, PROT_READ, MAP_PRIVATE, policyfd, 0);
+ if (policy == MAP_FAILED) {
+ log_error("mmap() failed (%m), freezing");
+ result = -errno;
+ goto out;
+ }
+
+ written = loop_write(imafd, policy, (size_t)policy_size, false);
+ if (written != policy_size) {
+ log_error("Failed to load the IMA custom policy file %s (%m), "
+ "ignoring.", IMA_POLICY_PATH);
+ goto out_mmap;
+ }
+
+ log_info("Successfully loaded the IMA custom policy %s.",
+ IMA_POLICY_PATH);
+out_mmap:
+ munmap(policy, policy_size);
+out:
+ if (policyfd >= 0)
+ close_nointr_nofail(policyfd);
+ if (imafd >= 0)
+ close_nointr_nofail(imafd);
+ if (result)
+ return result;
+#endif /* HAVE_IMA */
+
+ return 0;
+}
diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h
new file mode 100644
index 0000000..7d677cf
--- /dev/null
+++ b/src/core/ima-setup.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooimasetuphfoo
+#define fooimasetuphfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
+ TORSEC group -- http://security.polito.it
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int ima_setup(void);
+
+#endif
diff --git a/src/core/initreq.h b/src/core/initreq.h
new file mode 100644
index 0000000..859042c
--- /dev/null
+++ b/src/core/initreq.h
@@ -0,0 +1,77 @@
+/*
+ * initreq.h Interface to talk to init through /dev/initctl.
+ *
+ * Copyright (C) 1995-2004 Miquel van Smoorenburg
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
+ *
+ */
+#ifndef _INITREQ_H
+#define _INITREQ_H
+
+#include <sys/param.h>
+
+#if defined(__FreeBSD_kernel__)
+# define INIT_FIFO "/etc/.initctl"
+#else
+# define INIT_FIFO "/dev/initctl"
+#endif
+
+#define INIT_MAGIC 0x03091969
+#define INIT_CMD_START 0
+#define INIT_CMD_RUNLVL 1
+#define INIT_CMD_POWERFAIL 2
+#define INIT_CMD_POWERFAILNOW 3
+#define INIT_CMD_POWEROK 4
+#define INIT_CMD_BSD 5
+#define INIT_CMD_SETENV 6
+#define INIT_CMD_UNSETENV 7
+
+#define INIT_CMD_CHANGECONS 12345
+
+#ifdef MAXHOSTNAMELEN
+# define INITRQ_HLEN MAXHOSTNAMELEN
+#else
+# define INITRQ_HLEN 64
+#endif
+
+/*
+ * This is what BSD 4.4 uses when talking to init.
+ * Linux doesn't use this right now.
+ */
+struct init_request_bsd {
+ char gen_id[8]; /* Beats me.. telnetd uses "fe" */
+ char tty_id[16]; /* Tty name minus /dev/tty */
+ char host[INITRQ_HLEN]; /* Hostname */
+ char term_type[16]; /* Terminal type */
+ int signal; /* Signal to send */
+ int pid; /* Process to send to */
+ char exec_name[128]; /* Program to execute */
+ char reserved[128]; /* For future expansion. */
+};
+
+
+/*
+ * Because of legacy interfaces, "runlevel" and "sleeptime"
+ * aren't in a separate struct in the union.
+ *
+ * The weird sizes are because init expects the whole
+ * struct to be 384 bytes.
+ */
+struct init_request {
+ int magic; /* Magic number */
+ int cmd; /* What kind of request */
+ int runlevel; /* Runlevel to change to */
+ int sleeptime; /* Time between TERM and KILL */
+ union {
+ struct init_request_bsd bsd;
+ char data[368];
+ } i;
+};
+
+#endif
diff --git a/src/core/job.c b/src/core/job.c
new file mode 100644
index 0000000..bae6ab9
--- /dev/null
+++ b/src/core/job.c
@@ -0,0 +1,701 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+
+#include "set.h"
+#include "unit.h"
+#include "macro.h"
+#include "strv.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "dbus-job.h"
+
+Job* job_new(Manager *m, JobType type, Unit *unit) {
+ Job *j;
+
+ assert(m);
+ assert(type < _JOB_TYPE_MAX);
+ assert(unit);
+
+ if (!(j = new0(Job, 1)))
+ return NULL;
+
+ j->manager = m;
+ j->id = m->current_job_id++;
+ j->type = type;
+ j->unit = unit;
+
+ j->timer_watch.type = WATCH_INVALID;
+
+ /* We don't link it here, that's what job_dependency() is for */
+
+ return j;
+}
+
+void job_free(Job *j) {
+ assert(j);
+
+ /* Detach from next 'bigger' objects */
+ if (j->installed) {
+ bus_job_send_removed_signal(j);
+
+ if (j->unit->job == j) {
+ j->unit->job = NULL;
+ unit_add_to_gc_queue(j->unit);
+ }
+
+ hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
+ j->installed = false;
+ }
+
+ /* Detach from next 'smaller' objects */
+ manager_transaction_unlink_job(j->manager, j, true);
+
+ if (j->in_run_queue)
+ LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
+
+ if (j->in_dbus_queue)
+ LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
+
+ if (j->timer_watch.type != WATCH_INVALID) {
+ assert(j->timer_watch.type == WATCH_JOB_TIMER);
+ assert(j->timer_watch.data.job == j);
+ assert(j->timer_watch.fd >= 0);
+
+ assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0);
+ close_nointr_nofail(j->timer_watch.fd);
+ }
+
+ free(j->bus_client);
+ free(j);
+}
+
+JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
+ JobDependency *l;
+
+ assert(object);
+
+ /* Adds a new job link, which encodes that the 'subject' job
+ * needs the 'object' job in some way. If 'subject' is NULL
+ * this means the 'anchor' job (i.e. the one the user
+ * explicitly asked for) is the requester. */
+
+ if (!(l = new0(JobDependency, 1)))
+ return NULL;
+
+ l->subject = subject;
+ l->object = object;
+ l->matters = matters;
+ l->conflicts = conflicts;
+
+ if (subject)
+ LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
+ else
+ LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
+
+ LIST_PREPEND(JobDependency, object, object->object_list, l);
+
+ return l;
+}
+
+void job_dependency_free(JobDependency *l) {
+ assert(l);
+
+ if (l->subject)
+ LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
+ else
+ LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
+
+ LIST_REMOVE(JobDependency, object, l->object->object_list, l);
+
+ free(l);
+}
+
+void job_dump(Job *j, FILE*f, const char *prefix) {
+ assert(j);
+ assert(f);
+
+ if (!prefix)
+ prefix = "";
+
+ fprintf(f,
+ "%s-> Job %u:\n"
+ "%s\tAction: %s -> %s\n"
+ "%s\tState: %s\n"
+ "%s\tForced: %s\n",
+ prefix, j->id,
+ prefix, j->unit->id, job_type_to_string(j->type),
+ prefix, job_state_to_string(j->state),
+ prefix, yes_no(j->override));
+}
+
+bool job_is_anchor(Job *j) {
+ JobDependency *l;
+
+ assert(j);
+
+ LIST_FOREACH(object, l, j->object_list)
+ if (!l->subject)
+ return true;
+
+ return false;
+}
+
+/*
+ * Merging is commutative, so imagine the matrix as symmetric. We store only
+ * its lower triangle to avoid duplication. We don't store the main diagonal,
+ * because A merged with A is simply A.
+ *
+ * Merging is associative! A merged with B merged with C is the same as
+ * A merged with C merged with B.
+ *
+ * Mergeability is transitive! If A can be merged with B and B with C then
+ * A also with C.
+ *
+ * Also, if A merged with B cannot be merged with C, then either A or B cannot
+ * be merged with C either.
+ */
+static const JobType job_merging_table[] = {
+/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD JOB_RELOAD_OR_START JOB_RESTART JOB_TRY_RESTART */
+/************************************************************************************************************************************/
+/*JOB_START */
+/*JOB_VERIFY_ACTIVE */ JOB_START,
+/*JOB_STOP */ -1, -1,
+/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1,
+/*JOB_RELOAD_OR_START*/ JOB_RELOAD_OR_START, JOB_RELOAD_OR_START, -1, JOB_RELOAD_OR_START,
+/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, JOB_RESTART,
+/*JOB_TRY_RESTART */ JOB_RESTART, JOB_TRY_RESTART, -1, JOB_TRY_RESTART, JOB_RESTART, JOB_RESTART,
+};
+
+JobType job_type_lookup_merge(JobType a, JobType b) {
+ assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX * (_JOB_TYPE_MAX - 1) / 2);
+ assert(a >= 0 && a < _JOB_TYPE_MAX);
+ assert(b >= 0 && b < _JOB_TYPE_MAX);
+
+ if (a == b)
+ return a;
+
+ if (a < b) {
+ JobType tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ return job_merging_table[(a - 1) * a / 2 + b];
+}
+
+bool job_type_is_redundant(JobType a, UnitActiveState b) {
+ switch (a) {
+
+ case JOB_START:
+ return
+ b == UNIT_ACTIVE ||
+ b == UNIT_RELOADING;
+
+ case JOB_STOP:
+ return
+ b == UNIT_INACTIVE ||
+ b == UNIT_FAILED;
+
+ case JOB_VERIFY_ACTIVE:
+ return
+ b == UNIT_ACTIVE ||
+ b == UNIT_RELOADING;
+
+ case JOB_RELOAD:
+ return
+ b == UNIT_RELOADING;
+
+ case JOB_RELOAD_OR_START:
+ return
+ b == UNIT_ACTIVATING ||
+ b == UNIT_RELOADING;
+
+ case JOB_RESTART:
+ return
+ b == UNIT_ACTIVATING;
+
+ case JOB_TRY_RESTART:
+ return
+ b == UNIT_ACTIVATING;
+
+ default:
+ assert_not_reached("Invalid job type");
+ }
+}
+
+bool job_is_runnable(Job *j) {
+ Iterator i;
+ Unit *other;
+
+ assert(j);
+ assert(j->installed);
+
+ /* Checks whether there is any job running for the units this
+ * job needs to be running after (in the case of a 'positive'
+ * job type) or before (in the case of a 'negative' job
+ * type. */
+
+ /* First check if there is an override */
+ if (j->ignore_order)
+ return true;
+
+ if (j->type == JOB_START ||
+ j->type == JOB_VERIFY_ACTIVE ||
+ j->type == JOB_RELOAD ||
+ j->type == JOB_RELOAD_OR_START) {
+
+ /* Immediate result is that the job is or might be
+ * started. In this case lets wait for the
+ * dependencies, regardless whether they are
+ * starting or stopping something. */
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
+ if (other->job)
+ return false;
+ }
+
+ /* Also, if something else is being stopped and we should
+ * change state after it, then lets wait. */
+
+ SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
+ if (other->job &&
+ (other->job->type == JOB_STOP ||
+ other->job->type == JOB_RESTART ||
+ other->job->type == JOB_TRY_RESTART))
+ return false;
+
+ /* This means that for a service a and a service b where b
+ * shall be started after a:
+ *
+ * start a + start b â 1st step start a, 2nd step start b
+ * start a + stop b â 1st step stop b, 2nd step start a
+ * stop a + start b â 1st step stop a, 2nd step start b
+ * stop a + stop b â 1st step stop b, 2nd step stop a
+ *
+ * This has the side effect that restarts are properly
+ * synchronized too. */
+
+ return true;
+}
+
+static void job_change_type(Job *j, JobType newtype) {
+ log_debug("Converting job %s/%s -> %s/%s",
+ j->unit->id, job_type_to_string(j->type),
+ j->unit->id, job_type_to_string(newtype));
+
+ j->type = newtype;
+}
+
+int job_run_and_invalidate(Job *j) {
+ int r;
+ uint32_t id;
+ Manager *m;
+
+ assert(j);
+ assert(j->installed);
+
+ if (j->in_run_queue) {
+ LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
+ j->in_run_queue = false;
+ }
+
+ if (j->state != JOB_WAITING)
+ return 0;
+
+ if (!job_is_runnable(j))
+ return -EAGAIN;
+
+ j->state = JOB_RUNNING;
+ job_add_to_dbus_queue(j);
+
+ /* While we execute this operation the job might go away (for
+ * example: because it is replaced by a new, conflicting
+ * job.) To make sure we don't access a freed job later on we
+ * store the id here, so that we can verify the job is still
+ * valid. */
+ id = j->id;
+ m = j->manager;
+
+ switch (j->type) {
+
+ case JOB_RELOAD_OR_START:
+ if (unit_active_state(j->unit) == UNIT_ACTIVE) {
+ job_change_type(j, JOB_RELOAD);
+ r = unit_reload(j->unit);
+ break;
+ }
+ job_change_type(j, JOB_START);
+ /* fall through */
+
+ case JOB_START:
+ r = unit_start(j->unit);
+
+ /* If this unit cannot be started, then simply wait */
+ if (r == -EBADR)
+ r = 0;
+ break;
+
+ case JOB_VERIFY_ACTIVE: {
+ UnitActiveState t = unit_active_state(j->unit);
+ if (UNIT_IS_ACTIVE_OR_RELOADING(t))
+ r = -EALREADY;
+ else if (t == UNIT_ACTIVATING)
+ r = -EAGAIN;
+ else
+ r = -ENOEXEC;
+ break;
+ }
+
+ case JOB_TRY_RESTART:
+ if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) {
+ r = -ENOEXEC;
+ break;
+ }
+ job_change_type(j, JOB_RESTART);
+ /* fall through */
+
+ case JOB_STOP:
+ case JOB_RESTART:
+ r = unit_stop(j->unit);
+
+ /* If this unit cannot stopped, then simply wait. */
+ if (r == -EBADR)
+ r = 0;
+ break;
+
+ case JOB_RELOAD:
+ r = unit_reload(j->unit);
+ break;
+
+ default:
+ assert_not_reached("Unknown job type");
+ }
+
+ if ((j = manager_get_job(m, id))) {
+ if (r == -EALREADY)
+ r = job_finish_and_invalidate(j, JOB_DONE);
+ else if (r == -ENOEXEC)
+ r = job_finish_and_invalidate(j, JOB_SKIPPED);
+ else if (r == -EAGAIN)
+ j->state = JOB_WAITING;
+ else if (r < 0)
+ r = job_finish_and_invalidate(j, JOB_FAILED);
+ }
+
+ return r;
+}
+
+static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+ assert(u);
+
+ if (t == JOB_START) {
+
+ switch (result) {
+
+ case JOB_DONE:
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
+ break;
+
+ case JOB_FAILED:
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
+ unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
+ break;
+
+ case JOB_DEPENDENCY:
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
+ break;
+
+ case JOB_TIMEOUT:
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
+ break;
+
+ default:
+ ;
+ }
+
+ } else if (t == JOB_STOP) {
+
+ switch (result) {
+
+ case JOB_TIMEOUT:
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
+ break;
+
+ case JOB_DONE:
+ case JOB_FAILED:
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
+ break;
+
+ default:
+ ;
+ }
+ }
+}
+
+int job_finish_and_invalidate(Job *j, JobResult result) {
+ Unit *u;
+ Unit *other;
+ JobType t;
+ Iterator i;
+ bool recursed = false;
+
+ assert(j);
+ assert(j->installed);
+
+ job_add_to_dbus_queue(j);
+
+ /* Patch restart jobs so that they become normal start jobs */
+ if (result == JOB_DONE && j->type == JOB_RESTART) {
+
+ job_change_type(j, JOB_START);
+ j->state = JOB_WAITING;
+
+ job_add_to_run_queue(j);
+
+ u = j->unit;
+ goto finish;
+ }
+
+ j->result = result;
+
+ log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
+
+ if (result == JOB_FAILED)
+ j->manager->n_failed_jobs ++;
+
+ u = j->unit;
+ t = j->type;
+ job_free(j);
+
+ job_print_status_message(u, t, result);
+
+ /* Fail depending jobs on failure */
+ if (result != JOB_DONE) {
+
+ if (t == JOB_START ||
+ t == JOB_VERIFY_ACTIVE ||
+ t == JOB_RELOAD_OR_START) {
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
+ if (other->job &&
+ (other->job->type == JOB_START ||
+ other->job->type == JOB_VERIFY_ACTIVE ||
+ other->job->type == JOB_RELOAD_OR_START)) {
+ job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
+ recursed = true;
+ }
+
+ SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
+ if (other->job &&
+ (other->job->type == JOB_START ||
+ other->job->type == JOB_VERIFY_ACTIVE ||
+ other->job->type == JOB_RELOAD_OR_START)) {
+ job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
+ recursed = true;
+ }
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
+ if (other->job &&
+ !other->job->override &&
+ (other->job->type == JOB_START ||
+ other->job->type == JOB_VERIFY_ACTIVE ||
+ other->job->type == JOB_RELOAD_OR_START)) {
+ job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
+ recursed = true;
+ }
+
+ } else if (t == JOB_STOP) {
+
+ SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
+ if (other->job &&
+ (other->job->type == JOB_START ||
+ other->job->type == JOB_VERIFY_ACTIVE ||
+ other->job->type == JOB_RELOAD_OR_START)) {
+ job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
+ recursed = true;
+ }
+ }
+ }
+
+ /* Trigger OnFailure dependencies that are not generated by
+ * the unit itself. We don't tread JOB_CANCELED as failure in
+ * this context. And JOB_FAILURE is already handled by the
+ * unit itself. */
+ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
+ log_notice("Job %s/%s failed with result '%s'.",
+ u->id,
+ job_type_to_string(t),
+ job_result_to_string(result));
+
+ unit_trigger_on_failure(u);
+ }
+
+finish:
+ /* Try to start the next jobs that can be started */
+ SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
+ if (other->job)
+ job_add_to_run_queue(other->job);
+ SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
+ if (other->job)
+ job_add_to_run_queue(other->job);
+
+ manager_check_finished(u->manager);
+
+ return recursed;
+}
+
+int job_start_timer(Job *j) {
+ struct itimerspec its;
+ struct epoll_event ev;
+ int fd, r;
+ assert(j);
+
+ if (j->unit->job_timeout <= 0 ||
+ j->timer_watch.type == WATCH_JOB_TIMER)
+ return 0;
+
+ assert(j->timer_watch.type == WATCH_INVALID);
+
+ if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ zero(its);
+ timespec_store(&its.it_value, j->unit->job_timeout);
+
+ if (timerfd_settime(fd, 0, &its, NULL) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ zero(ev);
+ ev.data.ptr = &j->timer_watch;
+ ev.events = EPOLLIN;
+
+ if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ j->timer_watch.type = WATCH_JOB_TIMER;
+ j->timer_watch.fd = fd;
+ j->timer_watch.data.job = j;
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+void job_add_to_run_queue(Job *j) {
+ assert(j);
+ assert(j->installed);
+
+ if (j->in_run_queue)
+ return;
+
+ LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
+ j->in_run_queue = true;
+}
+
+void job_add_to_dbus_queue(Job *j) {
+ assert(j);
+ assert(j->installed);
+
+ if (j->in_dbus_queue)
+ return;
+
+ /* We don't check if anybody is subscribed here, since this
+ * job might just have been created and not yet assigned to a
+ * connection/client. */
+
+ LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = true;
+}
+
+char *job_dbus_path(Job *j) {
+ char *p;
+
+ assert(j);
+
+ if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
+ return NULL;
+
+ return p;
+}
+
+void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
+ assert(j);
+ assert(w == &j->timer_watch);
+
+ log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
+ job_finish_and_invalidate(j, JOB_TIMEOUT);
+}
+
+static const char* const job_state_table[_JOB_STATE_MAX] = {
+ [JOB_WAITING] = "waiting",
+ [JOB_RUNNING] = "running"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
+
+static const char* const job_type_table[_JOB_TYPE_MAX] = {
+ [JOB_START] = "start",
+ [JOB_VERIFY_ACTIVE] = "verify-active",
+ [JOB_STOP] = "stop",
+ [JOB_RELOAD] = "reload",
+ [JOB_RELOAD_OR_START] = "reload-or-start",
+ [JOB_RESTART] = "restart",
+ [JOB_TRY_RESTART] = "try-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
+
+static const char* const job_mode_table[_JOB_MODE_MAX] = {
+ [JOB_FAIL] = "fail",
+ [JOB_REPLACE] = "replace",
+ [JOB_ISOLATE] = "isolate",
+ [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
+ [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
+
+static const char* const job_result_table[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = "done",
+ [JOB_CANCELED] = "canceled",
+ [JOB_TIMEOUT] = "timeout",
+ [JOB_FAILED] = "failed",
+ [JOB_DEPENDENCY] = "dependency",
+ [JOB_SKIPPED] = "skipped"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
diff --git a/src/core/job.h b/src/core/job.h
new file mode 100644
index 0000000..60a43e0
--- /dev/null
+++ b/src/core/job.h
@@ -0,0 +1,200 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foojobhfoo
+#define foojobhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include <errno.h>
+
+typedef struct Job Job;
+typedef struct JobDependency JobDependency;
+typedef enum JobType JobType;
+typedef enum JobState JobState;
+typedef enum JobMode JobMode;
+typedef enum JobResult JobResult;
+
+#include "manager.h"
+#include "unit.h"
+#include "hashmap.h"
+#include "list.h"
+
+/* Be careful when changing the job types! Adjust job_merging_table[] accordingly! */
+enum JobType {
+ JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */
+ JOB_VERIFY_ACTIVE,
+
+ JOB_STOP,
+
+ JOB_RELOAD, /* if running reload */
+ JOB_RELOAD_OR_START, /* if running reload, if not running start */
+
+ /* Note that restarts are first treated like JOB_STOP, but
+ * then instead of finishing are patched to become
+ * JOB_START. */
+ JOB_RESTART, /* if running stop, then start unconditionally */
+ JOB_TRY_RESTART, /* if running stop and then start */
+
+ _JOB_TYPE_MAX,
+ _JOB_TYPE_INVALID = -1
+};
+
+enum JobState {
+ JOB_WAITING,
+ JOB_RUNNING,
+ _JOB_STATE_MAX,
+ _JOB_STATE_INVALID = -1
+};
+
+enum JobMode {
+ JOB_FAIL, /* Fail if a conflicting job is already queued */
+ JOB_REPLACE, /* Replace an existing conflicting job */
+ JOB_ISOLATE, /* Start a unit, and stop all others */
+ JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
+ JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
+ _JOB_MODE_MAX,
+ _JOB_MODE_INVALID = -1
+};
+
+enum JobResult {
+ JOB_DONE,
+ JOB_CANCELED,
+ JOB_TIMEOUT,
+ JOB_FAILED,
+ JOB_DEPENDENCY,
+ JOB_SKIPPED,
+ _JOB_RESULT_MAX,
+ _JOB_RESULT_INVALID = -1
+};
+
+struct JobDependency {
+ /* Encodes that the 'subject' job needs the 'object' job in
+ * some way. This structure is used only while building a transaction. */
+ Job *subject;
+ Job *object;
+
+ LIST_FIELDS(JobDependency, subject);
+ LIST_FIELDS(JobDependency, object);
+
+ bool matters;
+ bool conflicts;
+};
+
+struct Job {
+ Manager *manager;
+ Unit *unit;
+
+ LIST_FIELDS(Job, transaction);
+ LIST_FIELDS(Job, run_queue);
+ LIST_FIELDS(Job, dbus_queue);
+
+ LIST_HEAD(JobDependency, subject_list);
+ LIST_HEAD(JobDependency, object_list);
+
+ /* Used for graph algs as a "I have been here" marker */
+ Job* marker;
+ unsigned generation;
+
+ uint32_t id;
+
+ JobType type;
+ JobState state;
+
+ Watch timer_watch;
+
+ /* Note that this bus object is not ref counted here. */
+ DBusConnection *bus;
+ char *bus_client;
+
+ JobResult result;
+
+ bool installed:1;
+ bool in_run_queue:1;
+ bool matters_to_anchor:1;
+ bool override:1;
+ bool in_dbus_queue:1;
+ bool sent_dbus_new_signal:1;
+ bool ignore_order:1;
+};
+
+Job* job_new(Manager *m, JobType type, Unit *unit);
+void job_free(Job *job);
+void job_dump(Job *j, FILE*f, const char *prefix);
+
+JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
+void job_dependency_free(JobDependency *l);
+
+bool job_is_anchor(Job *j);
+
+int job_merge(Job *j, Job *other);
+
+JobType job_type_lookup_merge(JobType a, JobType b);
+
+static inline int job_type_merge(JobType *a, JobType b) {
+ JobType t = job_type_lookup_merge(*a, b);
+ if (t < 0)
+ return -EEXIST;
+ *a = t;
+ return 0;
+}
+
+static inline bool job_type_is_mergeable(JobType a, JobType b) {
+ return job_type_lookup_merge(a, b) >= 0;
+}
+
+static inline bool job_type_is_conflicting(JobType a, JobType b) {
+ return !job_type_is_mergeable(a, b);
+}
+
+static inline bool job_type_is_superset(JobType a, JobType b) {
+ /* Checks whether operation a is a "superset" of b in its actions */
+ return a == job_type_lookup_merge(a, b);
+}
+
+bool job_type_is_redundant(JobType a, UnitActiveState b);
+
+bool job_is_runnable(Job *j);
+
+void job_add_to_run_queue(Job *j);
+void job_add_to_dbus_queue(Job *j);
+
+int job_start_timer(Job *j);
+void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w);
+
+int job_run_and_invalidate(Job *j);
+int job_finish_and_invalidate(Job *j, JobResult result);
+
+char *job_dbus_path(Job *j);
+
+const char* job_type_to_string(JobType t);
+JobType job_type_from_string(const char *s);
+
+const char* job_state_to_string(JobState t);
+JobState job_state_from_string(const char *s);
+
+const char* job_mode_to_string(JobMode t);
+JobMode job_mode_from_string(const char *s);
+
+const char* job_result_to_string(JobResult t);
+JobResult job_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
new file mode 100644
index 0000000..debf871
--- /dev/null
+++ b/src/core/kmod-setup.c
@@ -0,0 +1,96 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libkmod.h>
+
+#include "macro.h"
+#include "execute.h"
+
+#include "kmod-setup.h"
+
+static const char * const kmod_table[] = {
+ "autofs4", "/sys/class/misc/autofs",
+ "ipv6", "/sys/module/ipv6",
+ "unix", "/proc/net/unix"
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+static void systemd_kmod_log(void *data, int priority, const char *file, int line,
+ const char *fn, const char *format, va_list args)
+{
+ log_meta(priority, file, line, fn, format, args);
+}
+#pragma GCC diagnostic pop
+
+int kmod_setup(void) {
+ unsigned i;
+ struct kmod_ctx *ctx = NULL;
+ struct kmod_module *mod;
+ int err;
+
+ for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) {
+
+ if (access(kmod_table[i+1], F_OK) >= 0)
+ continue;
+
+ log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. "
+ "We'll now try to work around this by loading the module...",
+ kmod_table[i]);
+
+ if (!ctx) {
+ ctx = kmod_new(NULL, NULL);
+ if (!ctx) {
+ log_error("Failed to allocate memory for kmod");
+ return -ENOMEM;
+ }
+
+ kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+
+ kmod_load_resources(ctx);
+ }
+
+ err = kmod_module_new_from_name(ctx, kmod_table[i], &mod);
+ if (err < 0) {
+ log_error("Failed to load module '%s'", kmod_table[i]);
+ continue;
+ }
+
+ err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
+ if (err == 0)
+ log_info("Inserted module '%s'", kmod_module_get_name(mod));
+ else if (err == KMOD_PROBE_APPLY_BLACKLIST)
+ log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
+ else
+ log_error("Failed to insert '%s'", kmod_module_get_name(mod));
+
+ kmod_module_unref(mod);
+ }
+
+ if (ctx)
+ kmod_unref(ctx);
+
+ return 0;
+}
diff --git a/src/core/kmod-setup.h b/src/core/kmod-setup.h
new file mode 100644
index 0000000..496aef3
--- /dev/null
+++ b/src/core/kmod-setup.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fookmodsetuphfoo
+#define fookmodsetuphfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int kmod_setup(void);
+
+#endif
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
new file mode 100644
index 0000000..d869ee0
--- /dev/null
+++ b/src/core/load-dropin.c
@@ -0,0 +1,150 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "unit.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "strv.h"
+#include "unit-name.h"
+
+static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) {
+ DIR *d;
+ struct dirent *de;
+ int r;
+
+ assert(u);
+ assert(path);
+
+ d = opendir(path);
+ if (!d) {
+
+ if (errno == ENOENT)
+ return 0;
+
+ return -errno;
+ }
+
+ while ((de = readdir(d))) {
+ char *f;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ f = join(path, "/", de->d_name, NULL);
+ if (!f) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true);
+ free(f);
+
+ if (r < 0)
+ log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r));
+ }
+
+ r = 0;
+
+finish:
+ closedir(d);
+ return r;
+}
+
+static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) {
+ int r;
+ char *path;
+
+ assert(u);
+ assert(unit_path);
+ assert(name);
+ assert(suffix);
+
+ path = join(unit_path, "/", name, suffix, NULL);
+ if (!path)
+ return -ENOMEM;
+
+ if (u->manager->unit_path_cache &&
+ !set_get(u->manager->unit_path_cache, path))
+ r = 0;
+ else
+ r = iterate_dir(u, path, dependency);
+ free(path);
+
+ if (r < 0)
+ return r;
+
+ if (u->instance) {
+ char *template;
+ /* Also try the template dir */
+
+ template = unit_name_template(name);
+ if (!template)
+ return -ENOMEM;
+
+ path = join(unit_path, "/", template, suffix, NULL);
+ free(template);
+
+ if (!path)
+ return -ENOMEM;
+
+ if (u->manager->unit_path_cache &&
+ !set_get(u->manager->unit_path_cache, path))
+ r = 0;
+ else
+ r = iterate_dir(u, path, dependency);
+ free(path);
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int unit_load_dropin(Unit *u) {
+ Iterator i;
+ char *t;
+
+ assert(u);
+
+ /* Load dependencies from supplementary drop-in directories */
+
+ SET_FOREACH(t, u->names, i) {
+ char **p;
+
+ STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ int r;
+
+ r = process_dir(u, *p, t, ".wants", UNIT_WANTS);
+ if (r < 0)
+ return r;
+
+ r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
new file mode 100644
index 0000000..cf3a799
--- /dev/null
+++ b/src/core/load-dropin.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooloaddropinhfoo
+#define fooloaddropinhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "unit.h"
+
+/* Read service data supplementary drop-in directories */
+
+int unit_load_dropin(Unit *u);
+
+#endif
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
new file mode 100644
index 0000000..637c82b
--- /dev/null
+++ b/src/core/load-fragment.c
@@ -0,0 +1,2445 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/oom.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/mount.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "unit.h"
+#include "strv.h"
+#include "conf-parser.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "ioprio.h"
+#include "securebits.h"
+#include "missing.h"
+#include "unit-name.h"
+#include "bus-errors.h"
+#include "utf8.h"
+
+#ifndef HAVE_SYSV_COMPAT
+int config_parse_warn_compat(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
+ return 0;
+}
+#endif
+
+int config_parse_unit_deps(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ UnitDependency d = ltype;
+ Unit *u = userdata;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t, *k;
+ int r;
+
+ t = strndup(w, l);
+ if (!t)
+ return -ENOMEM;
+
+ k = unit_name_printf(u, t);
+ free(t);
+ if (!k)
+ return -ENOMEM;
+
+ r = unit_add_dependency_by_name(u, d, k, NULL, true);
+ if (r < 0)
+ log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
+
+ free(k);
+ }
+
+ return 0;
+}
+
+int config_parse_unit_names(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t, *k;
+ int r;
+
+ t = strndup(w, l);
+ if (!t)
+ return -ENOMEM;
+
+ k = unit_name_printf(u, t);
+ free(t);
+ if (!k)
+ return -ENOMEM;
+
+ r = unit_merge_by_name(u, k);
+ if (r < 0)
+ log_error("Failed to add name %s, ignoring: %s", k, strerror(-r));
+
+ free(k);
+ }
+
+ return 0;
+}
+
+int config_parse_unit_string_printf(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char *k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ k = unit_full_printf(u, rvalue);
+ if (!k)
+ return -ENOMEM;
+
+ r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
+ free (k);
+
+ return r;
+}
+
+int config_parse_unit_strv_printf(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char *k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ k = unit_full_printf(u, rvalue);
+ if (!k)
+ return -ENOMEM;
+
+ r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
+ free(k);
+
+ return r;
+}
+
+int config_parse_unit_path_printf(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char *k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ k = unit_full_printf(u, rvalue);
+ if (!k)
+ return -ENOMEM;
+
+ r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
+ free(k);
+
+ return r;
+}
+
+int config_parse_socket_listen(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ SocketPort *p, *tail;
+ Socket *s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = SOCKET(data);
+
+ p = new0(SocketPort, 1);
+ if (!p)
+ return -ENOMEM;
+
+ if (streq(lvalue, "ListenFIFO")) {
+ p->type = SOCKET_FIFO;
+
+ if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(p->path);
+
+ } else if (streq(lvalue, "ListenSpecial")) {
+ p->type = SOCKET_SPECIAL;
+
+ if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(p->path);
+
+ } else if (streq(lvalue, "ListenMessageQueue")) {
+
+ p->type = SOCKET_MQUEUE;
+
+ if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(p->path);
+
+ } else if (streq(lvalue, "ListenNetlink")) {
+ char *k;
+ int r;
+
+ p->type = SOCKET_SOCKET;
+ k = unit_full_printf(UNIT(s), rvalue);
+ r = socket_address_parse_netlink(&p->address, k);
+ free(k);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ free(p);
+ return 0;
+ }
+
+ } else {
+ char *k;
+ int r;
+
+ p->type = SOCKET_SOCKET;
+ k = unit_full_printf(UNIT(s), rvalue);
+ r = socket_address_parse(&p->address, k);
+ free(k);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ free(p);
+ return 0;
+ }
+
+ if (streq(lvalue, "ListenStream"))
+ p->address.type = SOCK_STREAM;
+ else if (streq(lvalue, "ListenDatagram"))
+ p->address.type = SOCK_DGRAM;
+ else {
+ assert(streq(lvalue, "ListenSequentialPacket"));
+ p->address.type = SOCK_SEQPACKET;
+ }
+
+ if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
+ log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
+ free(p);
+ return 0;
+ }
+ }
+
+ p->fd = -1;
+
+ if (s->ports) {
+ LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
+ LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
+ } else
+ LIST_PREPEND(SocketPort, port, s->ports, p);
+
+ return 0;
+}
+
+int config_parse_socket_bind(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Socket *s;
+ SocketAddressBindIPv6Only b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = SOCKET(data);
+
+ if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) {
+ int r;
+
+ if ((r = parse_boolean(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
+ } else
+ s->bind_ipv6_only = b;
+
+ return 0;
+}
+
+int config_parse_exec_nice(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int priority;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &priority) < 0) {
+ log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
+ return 0;
+ }
+
+ if (priority < PRIO_MIN || priority >= PRIO_MAX) {
+ log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->nice = priority;
+ c->nice_set = true;
+
+ return 0;
+}
+
+int config_parse_exec_oom_score_adjust(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int oa;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &oa) < 0) {
+ log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
+ log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->oom_score_adjust = oa;
+ c->oom_score_adjust_set = true;
+
+ return 0;
+}
+
+int config_parse_exec(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecCommand **e = data, *nce;
+ char *path, **n;
+ unsigned k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(e);
+
+ /* We accept an absolute path as first argument, or
+ * alternatively an absolute prefixed with @ to allow
+ * overriding of argv[0]. */
+
+ e += ltype;
+
+ for (;;) {
+ char *w;
+ size_t l;
+ char *state;
+ bool honour_argv0 = false, ignore = false;
+
+ path = NULL;
+ nce = NULL;
+ n = NULL;
+
+ rvalue += strspn(rvalue, WHITESPACE);
+
+ if (rvalue[0] == 0)
+ break;
+
+ if (rvalue[0] == '-') {
+ ignore = true;
+ rvalue ++;
+ }
+
+ if (rvalue[0] == '@') {
+ honour_argv0 = true;
+ rvalue ++;
+ }
+
+ if (*rvalue != '/') {
+ log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ k = 0;
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ break;
+
+ k++;
+ }
+
+ n = new(char*, k + !honour_argv0);
+ if (!n)
+ return -ENOMEM;
+
+ k = 0;
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ break;
+
+ if (honour_argv0 && w == rvalue) {
+ assert(!path);
+
+ path = strndup(w, l);
+ if (!path) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!utf8_is_valid(path)) {
+ log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ r = 0;
+ goto fail;
+ }
+
+ } else {
+ char *c;
+
+ c = n[k++] = cunescape_length(w, l);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!utf8_is_valid(c)) {
+ log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ r = 0;
+ goto fail;
+ }
+ }
+ }
+
+ n[k] = NULL;
+
+ if (!n[0]) {
+ log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
+ r = 0;
+ goto fail;
+ }
+
+ if (!path) {
+ path = strdup(n[0]);
+ if (!path) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ assert(path_is_absolute(path));
+
+ nce = new0(ExecCommand, 1);
+ if (!nce) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ nce->argv = n;
+ nce->path = path;
+ nce->ignore = ignore;
+
+ path_kill_slashes(nce->path);
+
+ exec_command_append_list(e, nce);
+
+ rvalue = state;
+ }
+
+ return 0;
+
+fail:
+ n[k] = NULL;
+ strv_free(n);
+ free(path);
+ free(nce);
+
+ return r;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+
+int config_parse_socket_bindtodevice(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Socket *s = data;
+ char *n;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (rvalue[0] && !streq(rvalue, "*")) {
+ if (!(n = strdup(rvalue)))
+ return -ENOMEM;
+ } else
+ n = NULL;
+
+ free(s->bind_to_device);
+ s->bind_to_device = n;
+
+ return 0;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
+
+int config_parse_facility(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+
+ int *o = data, x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((x = log_facility_unshifted_from_string(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *o = (x << 3) | LOG_PRI(*o);
+
+ return 0;
+}
+
+int config_parse_level(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+
+ int *o = data, x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((x = log_level_from_string(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *o = (*o & LOG_FACMASK) | x;
+ return 0;
+}
+
+int config_parse_exec_io_class(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((x = ioprio_class_from_string(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
+ c->ioprio_set = true;
+
+ return 0;
+}
+
+int config_parse_exec_io_priority(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
+ log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
+ c->ioprio_set = true;
+
+ return 0;
+}
+
+int config_parse_exec_cpu_sched_policy(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+
+ ExecContext *c = data;
+ int x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((x = sched_policy_from_string(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->cpu_sched_policy = x;
+ c->cpu_sched_set = true;
+
+ return 0;
+}
+
+int config_parse_exec_cpu_sched_prio(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* On Linux RR/FIFO have the same range */
+ if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
+ log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->cpu_sched_priority = i;
+ c->cpu_sched_set = true;
+
+ return 0;
+}
+
+int config_parse_exec_cpu_affinity(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t;
+ int r;
+ unsigned cpu;
+
+ if (!(t = strndup(w, l)))
+ return -ENOMEM;
+
+ r = safe_atou(t, &cpu);
+ free(t);
+
+ if (!(c->cpuset))
+ if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus)))
+ return -ENOMEM;
+
+ if (r < 0 || cpu >= c->cpuset_ncpus) {
+ log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
+ }
+
+ return 0;
+}
+
+int config_parse_exec_capabilities(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ cap_t cap;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (!(cap = cap_from_text(rvalue))) {
+ if (errno == ENOMEM)
+ return -ENOMEM;
+
+ log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (c->capabilities)
+ cap_free(c->capabilities);
+ c->capabilities = cap;
+
+ return 0;
+}
+
+int config_parse_exec_secure_bits(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ char *w;
+ size_t l;
+ char *state;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ if (first_word(w, "keep-caps"))
+ c->secure_bits |= SECURE_KEEP_CAPS;
+ else if (first_word(w, "keep-caps-locked"))
+ c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
+ else if (first_word(w, "no-setuid-fixup"))
+ c->secure_bits |= SECURE_NO_SETUID_FIXUP;
+ else if (first_word(w, "no-setuid-fixup-locked"))
+ c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
+ else if (first_word(w, "noroot"))
+ c->secure_bits |= SECURE_NOROOT;
+ else if (first_word(w, "noroot-locked"))
+ c->secure_bits |= SECURE_NOROOT_LOCKED;
+ else {
+ log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int config_parse_exec_bounding_set(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ char *w;
+ size_t l;
+ char *state;
+ bool invert = false;
+ uint64_t sum = 0;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (rvalue[0] == '~') {
+ invert = true;
+ rvalue++;
+ }
+
+ /* Note that we store this inverted internally, since the
+ * kernel wants it like this. But we actually expose it
+ * non-inverted everywhere to have a fully normalized
+ * interface. */
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t;
+ int r;
+ cap_value_t cap;
+
+ if (!(t = strndup(w, l)))
+ return -ENOMEM;
+
+ r = cap_from_name(t, &cap);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
+ }
+
+ if (invert)
+ c->capability_bounding_set_drop |= sum;
+ else
+ c->capability_bounding_set_drop |= ~sum;
+
+ return 0;
+}
+
+int config_parse_exec_timer_slack_nsec(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ unsigned long u;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atolu(rvalue, &u) < 0) {
+ log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c->timer_slack_nsec = u;
+
+ return 0;
+}
+
+int config_parse_limit(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ struct rlimit **rl = data;
+ unsigned long long u;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ rl += ltype;
+
+ if (streq(rvalue, "infinity"))
+ u = (unsigned long long) RLIM_INFINITY;
+ else if (safe_atollu(rvalue, &u) < 0) {
+ log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!*rl)
+ if (!(*rl = new(struct rlimit, 1)))
+ return -ENOMEM;
+
+ (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
+ return 0;
+}
+
+int config_parse_unit_cgroup(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char *w;
+ size_t l;
+ char *state;
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t, *k;
+ int r;
+
+ t = strndup(w, l);
+ if (!t)
+ return -ENOMEM;
+
+ k = unit_full_printf(u, t);
+ free(t);
+
+ if (!k)
+ return -ENOMEM;
+
+ t = cunescape(k);
+ free(k);
+
+ if (!t)
+ return -ENOMEM;
+
+ r = unit_add_cgroup_from_text(u, t);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_SYSV_COMPAT
+int config_parse_sysv_priority(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *priority = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &i) < 0 || i < 0) {
+ log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *priority = (int) i;
+ return 0;
+}
+#endif
+
+int config_parse_fsck_passno(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *passno = data;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atoi(rvalue, &i) || i < 0) {
+ log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *passno = (int) i;
+ return 0;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
+
+int config_parse_kill_signal(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *sig = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(sig);
+
+ if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
+ log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *sig = r;
+ return 0;
+}
+
+int config_parse_exec_mount_flags(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ char *w;
+ size_t l;
+ char *state;
+ unsigned long flags = 0;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ if (strncmp(w, "shared", MAX(l, 6U)) == 0)
+ flags |= MS_SHARED;
+ else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
+ flags |= MS_SLAVE;
+ else if (strncmp(w, "private", MAX(l, 7U)) == 0)
+ flags |= MS_PRIVATE;
+ else {
+ log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+ }
+
+ c->mount_flags = flags;
+ return 0;
+}
+
+int config_parse_timer(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Timer *t = data;
+ usec_t u;
+ TimerValue *v;
+ TimerBase b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((b = timer_base_from_string(lvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
+ return 0;
+ }
+
+ if (parse_usec(rvalue, &u) < 0) {
+ log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(v = new0(TimerValue, 1)))
+ return -ENOMEM;
+
+ v->base = b;
+ v->value = u;
+
+ LIST_PREPEND(TimerValue, value, t->values, v);
+
+ return 0;
+}
+
+int config_parse_timer_unit(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Timer *t = data;
+ int r;
+ DBusError error;
+ Unit *u;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ dbus_error_init(&error);
+
+ if (endswith(rvalue, ".timer")) {
+ log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
+ if (r < 0) {
+ log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ dbus_error_free(&error);
+ return 0;
+ }
+
+ unit_ref_set(&t->unit, u);
+
+ return 0;
+}
+
+int config_parse_path_spec(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Path *p = data;
+ PathSpec *s;
+ PathType b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((b = path_type_from_string(lvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
+ return 0;
+ }
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(s = new0(PathSpec, 1)))
+ return -ENOMEM;
+
+ if (!(s->path = strdup(rvalue))) {
+ free(s);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(s->path);
+
+ s->type = b;
+ s->inotify_fd = -1;
+
+ LIST_PREPEND(PathSpec, spec, p->specs, s);
+
+ return 0;
+}
+
+int config_parse_path_unit(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Path *t = data;
+ int r;
+ DBusError error;
+ Unit *u;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ dbus_error_init(&error);
+
+ if (endswith(rvalue, ".path")) {
+ log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) {
+ log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ dbus_error_free(&error);
+ return 0;
+ }
+
+ unit_ref_set(&t->unit, u);
+
+ return 0;
+}
+
+int config_parse_socket_service(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Socket *s = data;
+ int r;
+ DBusError error;
+ Unit *x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ dbus_error_init(&error);
+
+ if (!endswith(rvalue, ".service")) {
+ log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
+ if (r < 0) {
+ log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ dbus_error_free(&error);
+ return 0;
+ }
+
+ unit_ref_set(&s->service, x);
+
+ return 0;
+}
+
+int config_parse_service_sockets(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Service *s = data;
+ int r;
+ char *state, *w;
+ size_t l;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ char *t, *k;
+
+ t = strndup(w, l);
+ if (!t)
+ return -ENOMEM;
+
+ k = unit_name_printf(UNIT(s), t);
+ free(t);
+
+ if (!k)
+ return -ENOMEM;
+
+ if (!endswith(k, ".socket")) {
+ log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
+ free(k);
+ continue;
+ }
+
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
+ if (r < 0)
+ log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
+
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
+ if (r < 0)
+ return r;
+
+ free(k);
+ }
+
+ return 0;
+}
+
+int config_parse_unit_env_file(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***env = data, **k;
+ Unit *u = userdata;
+ char *s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = unit_full_printf(u, rvalue);
+ if (!s)
+ return -ENOMEM;
+
+ if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
+ log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
+ free(s);
+ return 0;
+ }
+
+ k = strv_append(*env, s);
+ free(s);
+ if (!k)
+ return -ENOMEM;
+
+ strv_free(*env);
+ *env = k;
+
+ return 0;
+}
+
+int config_parse_ip_tos(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int *ip_tos = data, x;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((x = ip_tos_from_string(rvalue)) < 0)
+ if (safe_atoi(rvalue, &x) < 0) {
+ log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ *ip_tos = x;
+ return 0;
+}
+
+int config_parse_unit_condition_path(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ConditionType cond = ltype;
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
+ rvalue++;
+
+ negate = rvalue[0] == '!';
+ if (negate)
+ rvalue++;
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ c = condition_new(cond, rvalue, trigger, negate);
+ if (!c)
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+int config_parse_unit_condition_string(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ConditionType cond = ltype;
+ Unit *u = data;
+ bool trigger, negate;
+ Condition *c;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if (!(c = condition_new(cond, rvalue, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+int config_parse_unit_condition_null(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ Condition *c;
+ bool trigger, negate;
+ int b;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((trigger = rvalue[0] == '|'))
+ rvalue++;
+
+ if ((negate = rvalue[0] == '!'))
+ rvalue++;
+
+ if ((b = parse_boolean(rvalue)) < 0) {
+ log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!b)
+ negate = !negate;
+
+ if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
+ return -ENOMEM;
+
+ LIST_PREPEND(Condition, conditions, u->conditions, c);
+ return 0;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
+
+int config_parse_unit_cgroup_attr(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = data;
+ char **l;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ l = strv_split_quoted(rvalue);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2) {
+ log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
+ strv_free(l);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ Unit *u = data;
+ int r;
+ unsigned long ul;
+ char *t;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
+ log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (asprintf(&t, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ Unit *u = data;
+ int r;
+ off_t sz;
+ char *t;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
+ log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
+ return -ENOMEM;
+
+ r = unit_add_cgroup_attribute(u,
+ "memory",
+ streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
+ t, NULL);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int device_map(const char *controller, const char *name, const char *value, char **ret) {
+ char **l;
+
+ assert(controller);
+ assert(name);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ assert(strv_length(l) >= 1);
+
+ if (streq(l[0], "*")) {
+
+ if (asprintf(ret, "a *:*%s%s",
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
+ strv_free(l);
+ return -ENOMEM;
+ }
+
+ } else {
+ struct stat st;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ strv_free(l);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", l[0]);
+ strv_free(l);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%c %u:%u%s%s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
+
+ strv_free(l);
+ return -ENOMEM;
+ }
+ }
+
+ strv_free(l);
+ return 0;
+}
+
+int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ Unit *u = data;
+ char **l;
+ int r;
+ unsigned k;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ l = strv_split_quoted(rvalue);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2) {
+ log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
+ log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
+ log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+ strv_free(l);
+
+ r = unit_add_cgroup_attribute(u, "devices",
+ streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
+ rvalue, device_map);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
+ struct stat st;
+ char **l;
+ dev_t d;
+
+ assert(controller);
+ assert(name);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ assert(strv_length(l) == 2);
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ strv_free(l);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ d = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ d = st.st_dev;
+
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(d, &d);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
+ strv_free(l);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
+ strv_free(l);
+ return -ENOMEM;
+ }
+
+ strv_free(l);
+ return 0;
+}
+
+int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ Unit *u = data;
+ int r;
+ unsigned long ul;
+ const char *device = NULL, *weight;
+ unsigned k;
+ char *t, **l;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ l = strv_split_quoted(rvalue);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2) {
+ log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (k == 1)
+ weight = l[0];
+ else {
+ device = l[0];
+ weight = l[1];
+ }
+
+ if (device && !path_is_absolute(device)) {
+ log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
+ log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (device)
+ r = asprintf(&t, "%s %lu", device, ul);
+ else
+ r = asprintf(&t, "%lu", ul);
+ strv_free(l);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ if (device)
+ r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map);
+ else
+ r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ Unit *u = data;
+ int r;
+ off_t bytes;
+ unsigned k;
+ char *t, **l;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ l = strv_split_quoted(rvalue);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k != 2) {
+ log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (!path_is_absolute(l[0])) {
+ log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
+ log_error("[%s:%u] Failed to parse block IO bandwith value, ignoring: %s", filename, line, rvalue);
+ strv_free(l);
+ return 0;
+ }
+
+ r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
+ strv_free(l);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ r = unit_add_cgroup_attribute(u, "blkio",
+ streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
+ t, blkio_map);
+ free(t);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+
+#define FOLLOW_MAX 8
+
+static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
+ unsigned c = 0;
+ int fd, r;
+ FILE *f;
+ char *id = NULL;
+
+ assert(filename);
+ assert(*filename);
+ assert(_f);
+ assert(names);
+
+ /* This will update the filename pointer if the loaded file is
+ * reached by a symlink. The old string will be freed. */
+
+ for (;;) {
+ char *target, *name;
+
+ if (c++ >= FOLLOW_MAX)
+ return -ELOOP;
+
+ path_kill_slashes(*filename);
+
+ /* Add the file name we are currently looking at to
+ * the names of this unit, but only if it is a valid
+ * unit name. */
+ name = file_name_from_path(*filename);
+
+ if (unit_name_is_valid(name, true)) {
+
+ id = set_get(names, name);
+ if (!id) {
+ id = strdup(name);
+ if (!id)
+ return -ENOMEM;
+
+ r = set_put(names, id);
+ if (r < 0) {
+ free(id);
+ return r;
+ }
+ }
+ }
+
+ /* Try to open the file name, but don't if its a symlink */
+ if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
+ break;
+
+ if (errno != ELOOP)
+ return -errno;
+
+ /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
+ if ((r = readlink_and_make_absolute(*filename, &target)) < 0)
+ return r;
+
+ free(*filename);
+ *filename = target;
+ }
+
+ if (!(f = fdopen(fd, "re"))) {
+ r = -errno;
+ close_nointr_nofail(fd);
+ return r;
+ }
+
+ *_f = f;
+ *_final = id;
+ return 0;
+}
+
+static int merge_by_names(Unit **u, Set *names, const char *id) {
+ char *k;
+ int r;
+
+ assert(u);
+ assert(*u);
+ assert(names);
+
+ /* Let's try to add in all symlink names we found */
+ while ((k = set_steal_first(names))) {
+
+ /* First try to merge in the other name into our
+ * unit */
+ if ((r = unit_merge_by_name(*u, k)) < 0) {
+ Unit *other;
+
+ /* Hmm, we couldn't merge the other unit into
+ * ours? Then let's try it the other way
+ * round */
+
+ other = manager_get_unit((*u)->manager, k);
+ free(k);
+
+ if (other)
+ if ((r = unit_merge(other, *u)) >= 0) {
+ *u = other;
+ return merge_by_names(u, names, NULL);
+ }
+
+ return r;
+ }
+
+ if (id == k)
+ unit_choose_id(*u, id);
+
+ free(k);
+ }
+
+ return 0;
+}
+
+static int load_from_path(Unit *u, const char *path) {
+ int r;
+ Set *symlink_names;
+ FILE *f = NULL;
+ char *filename = NULL, *id = NULL;
+ Unit *merged;
+ struct stat st;
+
+ assert(u);
+ assert(path);
+
+ symlink_names = set_new(string_hash_func, string_compare_func);
+ if (!symlink_names)
+ return -ENOMEM;
+
+ if (path_is_absolute(path)) {
+
+ if (!(filename = strdup(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
+ free(filename);
+ filename = NULL;
+
+ if (r != -ENOENT)
+ goto finish;
+ }
+
+ } else {
+ char **p;
+
+ STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+
+ /* Instead of opening the path right away, we manually
+ * follow all symlinks and add their name to our unit
+ * name set while doing so */
+ if (!(filename = path_make_absolute(path, *p))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (u->manager->unit_path_cache &&
+ !set_get(u->manager->unit_path_cache, filename))
+ r = -ENOENT;
+ else
+ r = open_follow(&filename, &f, symlink_names, &id);
+
+ if (r < 0) {
+ char *sn;
+
+ free(filename);
+ filename = NULL;
+
+ if (r != -ENOENT)
+ goto finish;
+
+ /* Empty the symlink names for the next run */
+ while ((sn = set_steal_first(symlink_names)))
+ free(sn);
+
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (!filename) {
+ /* Hmm, no suitable file found? */
+ r = 0;
+ goto finish;
+ }
+
+ merged = u;
+ if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
+ goto finish;
+
+ if (merged != u) {
+ u->load_state = UNIT_MERGED;
+ r = 0;
+ goto finish;
+ }
+
+ zero(st);
+ if (fstat(fileno(f), &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (null_or_empty(&st))
+ u->load_state = UNIT_MASKED;
+ else {
+ /* Now, parse the file contents */
+ r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
+ if (r < 0)
+ goto finish;
+
+ u->load_state = UNIT_LOADED;
+ }
+
+ free(u->fragment_path);
+ u->fragment_path = filename;
+ filename = NULL;
+
+ u->fragment_mtime = timespec_load(&st.st_mtim);
+
+ r = 0;
+
+finish:
+ set_free_free(symlink_names);
+ free(filename);
+
+ if (f)
+ fclose(f);
+
+ return r;
+}
+
+int unit_load_fragment(Unit *u) {
+ int r;
+ Iterator i;
+ const char *t;
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+ assert(u->id);
+
+ /* First, try to find the unit under its id. We always look
+ * for unit files in the default directories, to make it easy
+ * to override things by placing things in /etc/systemd/system */
+ if ((r = load_from_path(u, u->id)) < 0)
+ return r;
+
+ /* Try to find an alias we can load this with */
+ if (u->load_state == UNIT_STUB)
+ SET_FOREACH(t, u->names, i) {
+
+ if (t == u->id)
+ continue;
+
+ if ((r = load_from_path(u, t)) < 0)
+ return r;
+
+ if (u->load_state != UNIT_STUB)
+ break;
+ }
+
+ /* And now, try looking for it under the suggested (originally linked) path */
+ if (u->load_state == UNIT_STUB && u->fragment_path) {
+
+ if ((r = load_from_path(u, u->fragment_path)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_STUB) {
+ /* Hmm, this didn't work? Then let's get rid
+ * of the fragment path stored for us, so that
+ * we don't point to an invalid location. */
+ free(u->fragment_path);
+ u->fragment_path = NULL;
+ }
+ }
+
+ /* Look for a template */
+ if (u->load_state == UNIT_STUB && u->instance) {
+ char *k;
+
+ if (!(k = unit_name_template(u->id)))
+ return -ENOMEM;
+
+ r = load_from_path(u, k);
+ free(k);
+
+ if (r < 0)
+ return r;
+
+ if (u->load_state == UNIT_STUB)
+ SET_FOREACH(t, u->names, i) {
+
+ if (t == u->id)
+ continue;
+
+ if (!(k = unit_name_template(t)))
+ return -ENOMEM;
+
+ r = load_from_path(u, k);
+ free(k);
+
+ if (r < 0)
+ return r;
+
+ if (u->load_state != UNIT_STUB)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void unit_dump_config_items(FILE *f) {
+ static const struct {
+ const ConfigParserCallback callback;
+ const char *rvalue;
+ } table[] = {
+ { config_parse_int, "INTEGER" },
+ { config_parse_unsigned, "UNSIGNED" },
+ { config_parse_bytes_size, "SIZE" },
+ { config_parse_bool, "BOOLEAN" },
+ { config_parse_string, "STRING" },
+ { config_parse_path, "PATH" },
+ { config_parse_unit_path_printf, "PATH" },
+ { config_parse_strv, "STRING [...]" },
+ { config_parse_exec_nice, "NICE" },
+ { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
+ { config_parse_exec_io_class, "IOCLASS" },
+ { config_parse_exec_io_priority, "IOPRIORITY" },
+ { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
+ { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
+ { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
+ { config_parse_mode, "MODE" },
+ { config_parse_unit_env_file, "FILE" },
+ { config_parse_output, "OUTPUT" },
+ { config_parse_input, "INPUT" },
+ { config_parse_facility, "FACILITY" },
+ { config_parse_level, "LEVEL" },
+ { config_parse_exec_capabilities, "CAPABILITIES" },
+ { config_parse_exec_secure_bits, "SECUREBITS" },
+ { config_parse_exec_bounding_set, "BOUNDINGSET" },
+ { config_parse_exec_timer_slack_nsec, "TIMERSLACK" },
+ { config_parse_limit, "LIMIT" },
+ { config_parse_unit_cgroup, "CGROUP [...]" },
+ { config_parse_unit_deps, "UNIT [...]" },
+ { config_parse_unit_names, "UNIT [...]" },
+ { config_parse_exec, "PATH [ARGUMENT [...]]" },
+ { config_parse_service_type, "SERVICETYPE" },
+ { config_parse_service_restart, "SERVICERESTART" },
+#ifdef HAVE_SYSV_COMPAT
+ { config_parse_sysv_priority, "SYSVPRIORITY" },
+#else
+ { config_parse_warn_compat, "NOTSUPPORTED" },
+#endif
+ { config_parse_kill_mode, "KILLMODE" },
+ { config_parse_kill_signal, "SIGNAL" },
+ { config_parse_socket_listen, "SOCKET [...]" },
+ { config_parse_socket_bind, "SOCKETBIND" },
+ { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
+ { config_parse_usec, "SECONDS" },
+ { config_parse_path_strv, "PATH [...]" },
+ { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
+ { config_parse_unit_string_printf, "STRING" },
+ { config_parse_timer, "TIMER" },
+ { config_parse_timer_unit, "NAME" },
+ { config_parse_path_spec, "PATH" },
+ { config_parse_path_unit, "UNIT" },
+ { config_parse_notify_access, "ACCESS" },
+ { config_parse_ip_tos, "TOS" },
+ { config_parse_unit_condition_path, "CONDITION" },
+ { config_parse_unit_condition_string, "CONDITION" },
+ { config_parse_unit_condition_null, "CONDITION" },
+ };
+
+ const char *prev = NULL;
+ const char *i;
+
+ assert(f);
+
+ NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
+ const char *rvalue = "OTHER", *lvalue;
+ unsigned j;
+ size_t prefix_len;
+ const char *dot;
+ const ConfigPerfItem *p;
+
+ assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
+
+ dot = strchr(i, '.');
+ lvalue = dot ? dot + 1 : i;
+ prefix_len = dot-i;
+
+ if (dot)
+ if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
+ if (prev)
+ fputc('\n', f);
+
+ fprintf(f, "[%.*s]\n", (int) prefix_len, i);
+ }
+
+ for (j = 0; j < ELEMENTSOF(table); j++)
+ if (p->parse == table[j].callback) {
+ rvalue = table[j].rvalue;
+ break;
+ }
+
+ fprintf(f, "%s=%s\n", lvalue, rvalue);
+ prev = i;
+ }
+}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
new file mode 100644
index 0000000..79fc76d
--- /dev/null
+++ b/src/core/load-fragment.h
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooloadfragmenthfoo
+#define fooloadfragmenthfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "unit.h"
+
+/* Read service data from .desktop file style configuration fragments */
+
+int unit_load_fragment(Unit *u);
+
+void unit_dump_config_items(FILE *f);
+
+int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_names(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_timer_slack_nsec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+/* gperf prototypes */
+const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
+extern const char load_fragment_gperf_nulstr[];
+
+#endif
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
new file mode 100644
index 0000000..7f692e9
--- /dev/null
+++ b/src/core/locale-setup.c
@@ -0,0 +1,251 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "locale-setup.h"
+#include "util.h"
+#include "macro.h"
+#include "virt.h"
+
+enum {
+ /* We don't list LC_ALL here on purpose. People should be
+ * using LANG instead. */
+
+ VARIABLE_LANG,
+ VARIABLE_LANGUAGE,
+ VARIABLE_LC_CTYPE,
+ VARIABLE_LC_NUMERIC,
+ VARIABLE_LC_TIME,
+ VARIABLE_LC_COLLATE,
+ VARIABLE_LC_MONETARY,
+ VARIABLE_LC_MESSAGES,
+ VARIABLE_LC_PAPER,
+ VARIABLE_LC_NAME,
+ VARIABLE_LC_ADDRESS,
+ VARIABLE_LC_TELEPHONE,
+ VARIABLE_LC_MEASUREMENT,
+ VARIABLE_LC_IDENTIFICATION,
+ _VARIABLE_MAX
+};
+
+static const char * const variable_names[_VARIABLE_MAX] = {
+ [VARIABLE_LANG] = "LANG",
+ [VARIABLE_LANGUAGE] = "LANGUAGE",
+ [VARIABLE_LC_CTYPE] = "LC_CTYPE",
+ [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
+ [VARIABLE_LC_TIME] = "LC_TIME",
+ [VARIABLE_LC_COLLATE] = "LC_COLLATE",
+ [VARIABLE_LC_MONETARY] = "LC_MONETARY",
+ [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
+ [VARIABLE_LC_PAPER] = "LC_PAPER",
+ [VARIABLE_LC_NAME] = "LC_NAME",
+ [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
+ [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
+ [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
+ [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
+};
+
+int locale_setup(void) {
+ char *variables[_VARIABLE_MAX];
+ int r = 0, i;
+
+ zero(variables);
+
+ if (detect_container(NULL) <= 0)
+ if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
+#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
+ "LANG", &variables[VARIABLE_LANG],
+#endif
+ "locale.LANG", &variables[VARIABLE_LANG],
+ "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
+ "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
+ "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
+ }
+
+ /* Hmm, nothing set on the kernel cmd line? Then let's
+ * try /etc/locale.conf */
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/locale.conf", NEWLINE,
+ "LANG", &variables[VARIABLE_LANG],
+ "LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "LC_TIME", &variables[VARIABLE_LC_TIME],
+ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "LC_NAME", &variables[VARIABLE_LC_NAME],
+ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/locale.conf: %s", strerror(-r));
+ }
+
+#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MEEGO)
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+ "LANG", &variables[VARIABLE_LANG],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+ }
+
+#elif defined(TARGET_SUSE)
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/sysconfig/language", NEWLINE,
+ "RC_LANG", &variables[VARIABLE_LANG],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r));
+ }
+
+#elif defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/default/locale", NEWLINE,
+ "LANG", &variables[VARIABLE_LANG],
+ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "LC_TIME", &variables[VARIABLE_LC_TIME],
+ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "LC_NAME", &variables[VARIABLE_LC_NAME],
+ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/default/locale: %s", strerror(-r));
+ }
+
+#elif defined(TARGET_ARCH)
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/rc.conf", NEWLINE,
+ "LOCALE", &variables[VARIABLE_LANG],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
+ }
+
+#elif defined(TARGET_GENTOO)
+ /* Gentoo's openrc expects locale variables in /etc/env.d/
+ * These files are later compiled by env-update into shell
+ * export commands at /etc/profile.env, with variables being
+ * exported by openrc's runscript (so /etc/init.d/)
+ */
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/profile.env", NEWLINE,
+ "export LANG", &variables[VARIABLE_LANG],
+ "export LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "export LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "export LC_TIME", &variables[VARIABLE_LC_TIME],
+ "export LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "export LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "export LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "export LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "export LC_NAME", &variables[VARIABLE_LC_NAME],
+ "export LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "export LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "export LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/profile.env: %s", strerror(-r));
+ }
+#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA )
+ if (r <= 0 &&
+ (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+ "LANG", &variables[VARIABLE_LANG],
+ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "LC_TIME", &variables[VARIABLE_LC_TIME],
+ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "LC_NAME", &variables[VARIABLE_LC_NAME],
+ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+ }
+
+#endif
+
+ if (!variables[VARIABLE_LANG]) {
+ if (!(variables[VARIABLE_LANG] = strdup("C"))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ }
+
+ for (i = 0; i < _VARIABLE_MAX; i++) {
+
+ if (variables[i]) {
+ if (setenv(variable_names[i], variables[i], 1) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ } else
+ unsetenv(variable_names[i]);
+ }
+
+ r = 0;
+
+finish:
+ for (i = 0; i < _VARIABLE_MAX; i++)
+ free(variables[i]);
+
+ return r;
+}
diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h
new file mode 100644
index 0000000..09a6bc6
--- /dev/null
+++ b/src/core/locale-setup.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foolocalesetuphfoo
+#define foolocalesetuphfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int locale_setup(void);
+
+#endif
diff --git a/src/core/manager.c b/src/core/manager.c
new file mode 100644
index 0000000..312527a
--- /dev/null
+++ b/src/core/manager.c
@@ -0,0 +1,3235 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/reboot.h>
+#include <sys/ioctl.h>
+#include <linux/kd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+#include <systemd/sd-daemon.h>
+
+#include "manager.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "strv.h"
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "ratelimit.h"
+#include "cgroup.h"
+#include "mount-setup.h"
+#include "unit-name.h"
+#include "dbus-unit.h"
+#include "dbus-job.h"
+#include "missing.h"
+#include "path-lookup.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "exit-status.h"
+#include "virt.h"
+#include "watchdog.h"
+
+/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
+#define GC_QUEUE_ENTRIES_MAX 16
+
+/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
+#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
+
+/* Where clients shall send notification messages to */
+#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
+#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
+
+static int manager_setup_notify(Manager *m) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+ struct epoll_event ev;
+ int one = 1, r;
+ mode_t u;
+
+ assert(m);
+
+ m->notify_watch.type = WATCH_NOTIFY;
+ if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ log_error("Failed to allocate notification socket: %m");
+ return -errno;
+ }
+
+ zero(sa);
+ sa.sa.sa_family = AF_UNIX;
+
+ if (getpid() != 1)
+ snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
+ else {
+ unlink(NOTIFY_SOCKET_SYSTEM);
+ strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
+ }
+
+ if (sa.un.sun_path[0] == '@')
+ sa.un.sun_path[0] = 0;
+
+ u = umask(0111);
+ r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
+ umask(u);
+
+ if (r < 0) {
+ log_error("bind() failed: %m");
+ return -errno;
+ }
+
+ if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
+ log_error("SO_PASSCRED failed: %m");
+ return -errno;
+ }
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = &m->notify_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
+ return -errno;
+
+ if (sa.un.sun_path[0] == 0)
+ sa.un.sun_path[0] = '@';
+
+ if (!(m->notify_socket = strdup(sa.un.sun_path)))
+ return -ENOMEM;
+
+ log_debug("Using notification socket %s", m->notify_socket);
+
+ return 0;
+}
+
+static int enable_special_signals(Manager *m) {
+ int fd;
+
+ assert(m);
+
+ /* Enable that we get SIGINT on control-alt-del */
+ if (reboot(RB_DISABLE_CAD) < 0)
+ log_warning("Failed to enable ctrl-alt-del handling: %m");
+
+ if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ log_warning("Failed to open /dev/tty0: %m");
+ else {
+ /* Enable that we get SIGWINCH on kbrequest */
+ if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
+ log_warning("Failed to enable kbrequest handling: %s", strerror(errno));
+
+ close_nointr_nofail(fd);
+ }
+
+ return 0;
+}
+
+static int manager_setup_signals(Manager *m) {
+ sigset_t mask;
+ struct epoll_event ev;
+ struct sigaction sa;
+
+ assert(m);
+
+ /* We are not interested in SIGSTOP and friends. */
+ zero(sa);
+ sa.sa_handler = SIG_DFL;
+ sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
+ assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+
+ assert_se(sigemptyset(&mask) == 0);
+
+ 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 */
+ SIGRTMIN+13, /* systemd: Immediate halt */
+ SIGRTMIN+14, /* systemd: Immediate poweroff */
+ SIGRTMIN+15, /* systemd: Immediate reboot */
+ SIGRTMIN+16, /* systemd: Immediate kexec */
+ 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+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 */
+ -1);
+ assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+ m->signal_watch.type = WATCH_SIGNAL;
+ if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
+ return -errno;
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = &m->signal_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
+ return -errno;
+
+ if (m->running_as == MANAGER_SYSTEM)
+ return enable_special_signals(m);
+
+ return 0;
+}
+
+static void manager_strip_environment(Manager *m) {
+ assert(m);
+
+ /* Remove variables from the inherited set that are part of
+ * the container interface:
+ * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
+ strv_remove_prefix(m->environment, "container=");
+ strv_remove_prefix(m->environment, "container_");
+
+ /* Remove variables from the inherited set that are part of
+ * the initrd interface:
+ * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
+ strv_remove_prefix(m->environment, "RD_");
+}
+
+int manager_new(ManagerRunningAs running_as, Manager **_m) {
+ Manager *m;
+ int r = -ENOMEM;
+
+ assert(_m);
+ assert(running_as >= 0);
+ assert(running_as < _MANAGER_RUNNING_AS_MAX);
+
+ if (!(m = new0(Manager, 1)))
+ return -ENOMEM;
+
+ dual_timestamp_get(&m->startup_timestamp);
+
+ m->running_as = running_as;
+ m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
+ m->exit_code = _MANAGER_EXIT_CODE_INVALID;
+ m->pin_cgroupfs_fd = -1;
+
+#ifdef HAVE_AUDIT
+ m->audit_fd = -1;
+#endif
+
+ m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
+ m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
+
+ m->environment = strv_copy(environ);
+ if (!m->environment)
+ goto fail;
+
+ manager_strip_environment(m);
+
+ if (running_as == MANAGER_SYSTEM) {
+ m->default_controllers = strv_new("cpu", NULL);
+ if (!m->default_controllers)
+ goto fail;
+ }
+
+ if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
+ goto fail;
+
+ if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
+ goto fail;
+
+ if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
+ goto fail;
+
+ if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
+ goto fail;
+
+ if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
+ goto fail;
+
+ if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
+ goto fail;
+
+ if ((r = manager_setup_signals(m)) < 0)
+ goto fail;
+
+ if ((r = manager_setup_cgroup(m)) < 0)
+ goto fail;
+
+ if ((r = manager_setup_notify(m)) < 0)
+ goto fail;
+
+ /* Try to connect to the busses, if possible. */
+ if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0)
+ goto fail;
+
+#ifdef HAVE_AUDIT
+ if ((m->audit_fd = audit_open()) < 0 &&
+ /* If the kernel lacks netlink or audit support,
+ * don't worry about it. */
+ errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
+ log_error("Failed to connect to audit log: %m");
+#endif
+
+ m->taint_usr = dir_is_empty("/usr") > 0;
+
+ *_m = m;
+ return 0;
+
+fail:
+ manager_free(m);
+ return r;
+}
+
+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) {
+ Job *j;
+ Unit *u;
+
+ assert(m);
+
+ while ((j = hashmap_first(m->transaction_jobs)))
+ job_free(j);
+
+ 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->transaction_jobs));
+ 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->transaction_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);
+
+ 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;
+}
+
+static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
+ assert(m);
+ assert(j);
+
+ /* Deletes one job from the transaction */
+
+ manager_transaction_unlink_job(m, j, delete_dependencies);
+
+ if (!j->installed)
+ job_free(j);
+}
+
+static void transaction_delete_unit(Manager *m, Unit *u) {
+ Job *j;
+
+ /* Deletes all jobs associated with a certain unit from the
+ * transaction */
+
+ while ((j = hashmap_get(m->transaction_jobs, u)))
+ transaction_delete_job(m, j, true);
+}
+
+static void transaction_clean_dependencies(Manager *m) {
+ Iterator i;
+ Job *j;
+
+ assert(m);
+
+ /* Drops all dependencies of all installed jobs */
+
+ HASHMAP_FOREACH(j, m->jobs, i) {
+ while (j->subject_list)
+ job_dependency_free(j->subject_list);
+ while (j->object_list)
+ job_dependency_free(j->object_list);
+ }
+
+ assert(!m->transaction_anchor);
+}
+
+static void transaction_abort(Manager *m) {
+ Job *j;
+
+ assert(m);
+
+ while ((j = hashmap_first(m->transaction_jobs)))
+ if (j->installed)
+ transaction_delete_job(m, j, true);
+ else
+ job_free(j);
+
+ assert(hashmap_isempty(m->transaction_jobs));
+
+ transaction_clean_dependencies(m);
+}
+
+static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) {
+ JobDependency *l;
+
+ assert(m);
+
+ /* A recursive sweep through the graph that marks all units
+ * that matter to the anchor job, i.e. are directly or
+ * indirectly a dependency of the anchor job via paths that
+ * are fully marked as mattering. */
+
+ if (j)
+ l = j->subject_list;
+ else
+ l = m->transaction_anchor;
+
+ LIST_FOREACH(subject, l, l) {
+
+ /* This link does not matter */
+ if (!l->matters)
+ continue;
+
+ /* This unit has already been marked */
+ if (l->object->generation == generation)
+ continue;
+
+ l->object->matters_to_anchor = true;
+ l->object->generation = generation;
+
+ transaction_find_jobs_that_matter_to_anchor(m, l->object, generation);
+ }
+}
+
+static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) {
+ JobDependency *l, *last;
+
+ assert(j);
+ assert(other);
+ assert(j->unit == other->unit);
+ assert(!j->installed);
+
+ /* Merges 'other' into 'j' and then deletes j. */
+
+ j->type = t;
+ j->state = JOB_WAITING;
+ j->override = j->override || other->override;
+
+ j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
+
+ /* Patch us in as new owner of the JobDependency objects */
+ last = NULL;
+ LIST_FOREACH(subject, l, other->subject_list) {
+ assert(l->subject == other);
+ l->subject = j;
+ last = l;
+ }
+
+ /* Merge both lists */
+ if (last) {
+ last->subject_next = j->subject_list;
+ if (j->subject_list)
+ j->subject_list->subject_prev = last;
+ j->subject_list = other->subject_list;
+ }
+
+ /* Patch us in as new owner of the JobDependency objects */
+ last = NULL;
+ LIST_FOREACH(object, l, other->object_list) {
+ assert(l->object == other);
+ l->object = j;
+ last = l;
+ }
+
+ /* Merge both lists */
+ if (last) {
+ last->object_next = j->object_list;
+ if (j->object_list)
+ j->object_list->object_prev = last;
+ j->object_list = other->object_list;
+ }
+
+ /* Kill the other job */
+ other->subject_list = NULL;
+ other->object_list = NULL;
+ transaction_delete_job(m, other, true);
+}
+static bool job_is_conflicted_by(Job *j) {
+ JobDependency *l;
+
+ assert(j);
+
+ /* Returns true if this job is pulled in by a least one
+ * ConflictedBy dependency. */
+
+ LIST_FOREACH(object, l, j->object_list)
+ if (l->conflicts)
+ return true;
+
+ return false;
+}
+
+static int delete_one_unmergeable_job(Manager *m, Job *j) {
+ Job *k;
+
+ assert(j);
+
+ /* Tries to delete one item in the linked list
+ * j->transaction_next->transaction_next->... that conflicts
+ * with another one, in an attempt to make an inconsistent
+ * transaction work. */
+
+ /* We rely here on the fact that if a merged with b does not
+ * merge with c, either a or b merge with c neither */
+ LIST_FOREACH(transaction, j, j)
+ LIST_FOREACH(transaction, k, j->transaction_next) {
+ Job *d;
+
+ /* Is this one mergeable? Then skip it */
+ if (job_type_is_mergeable(j->type, k->type))
+ continue;
+
+ /* Ok, we found two that conflict, let's see if we can
+ * drop one of them */
+ if (!j->matters_to_anchor && !k->matters_to_anchor) {
+
+ /* Both jobs don't matter, so let's
+ * find the one that is smarter to
+ * remove. Let's think positive and
+ * rather remove stops then starts --
+ * except if something is being
+ * stopped because it is conflicted by
+ * another unit in which case we
+ * rather remove the start. */
+
+ log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
+ log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
+
+ if (j->type == JOB_STOP) {
+
+ if (job_is_conflicted_by(j))
+ d = k;
+ else
+ d = j;
+
+ } else if (k->type == JOB_STOP) {
+
+ if (job_is_conflicted_by(k))
+ d = j;
+ else
+ d = k;
+ } else
+ d = j;
+
+ } else if (!j->matters_to_anchor)
+ d = j;
+ else if (!k->matters_to_anchor)
+ d = k;
+ else
+ return -ENOEXEC;
+
+ /* Ok, we can drop one, so let's do so. */
+ log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
+ transaction_delete_job(m, d, true);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int transaction_merge_jobs(Manager *m, DBusError *e) {
+ Job *j;
+ Iterator i;
+ int r;
+
+ assert(m);
+
+ /* First step, check whether any of the jobs for one specific
+ * task conflict. If so, try to drop one of them. */
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ JobType t;
+ Job *k;
+
+ t = j->type;
+ LIST_FOREACH(transaction, k, j->transaction_next) {
+ if (job_type_merge(&t, k->type) >= 0)
+ continue;
+
+ /* OK, we could not merge all jobs for this
+ * action. Let's see if we can get rid of one
+ * of them */
+
+ if ((r = delete_one_unmergeable_job(m, j)) >= 0)
+ /* Ok, we managed to drop one, now
+ * let's ask our callers to call us
+ * again after garbage collecting */
+ return -EAGAIN;
+
+ /* We couldn't merge anything. Failure */
+ dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
+ job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
+ return r;
+ }
+ }
+
+ /* Second step, merge the jobs. */
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ JobType t = j->type;
+ Job *k;
+
+ /* Merge all transactions */
+ LIST_FOREACH(transaction, k, j->transaction_next)
+ assert_se(job_type_merge(&t, k->type) == 0);
+
+ /* If an active job is mergeable, merge it too */
+ if (j->unit->job)
+ job_type_merge(&t, j->unit->job->type); /* Might fail. Which is OK */
+
+ while ((k = j->transaction_next)) {
+ if (j->installed) {
+ transaction_merge_and_delete_job(m, k, j, t);
+ j = k;
+ } else
+ transaction_merge_and_delete_job(m, j, k, t);
+ }
+
+ if (j->unit->job && !j->installed)
+ transaction_merge_and_delete_job(m, j, j->unit->job, t);
+
+ assert(!j->transaction_next);
+ assert(!j->transaction_prev);
+ }
+
+ return 0;
+}
+
+static void transaction_drop_redundant(Manager *m) {
+ bool again;
+
+ assert(m);
+
+ /* Goes through the transaction and removes all jobs that are
+ * a noop */
+
+ do {
+ Job *j;
+ Iterator i;
+
+ again = false;
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ bool changes_something = false;
+ Job *k;
+
+ LIST_FOREACH(transaction, k, j) {
+
+ if (!job_is_anchor(k) &&
+ (k->installed || job_type_is_redundant(k->type, unit_active_state(k->unit))) &&
+ (!k->unit->job || !job_type_is_conflicting(k->type, k->unit->job->type)))
+ continue;
+
+ changes_something = true;
+ break;
+ }
+
+ if (changes_something)
+ continue;
+
+ /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
+ transaction_delete_job(m, j, false);
+ again = true;
+ break;
+ }
+
+ } while (again);
+}
+
+static bool unit_matters_to_anchor(Unit *u, Job *j) {
+ assert(u);
+ assert(!j->transaction_prev);
+
+ /* Checks whether at least one of the jobs for this unit
+ * matters to the anchor. */
+
+ LIST_FOREACH(transaction, j, j)
+ if (j->matters_to_anchor)
+ return true;
+
+ return false;
+}
+
+static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation, DBusError *e) {
+ Iterator i;
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(j);
+ assert(!j->transaction_prev);
+
+ /* Does a recursive sweep through the ordering graph, looking
+ * for a cycle. If we find cycle we try to break it. */
+
+ /* Have we seen this before? */
+ if (j->generation == generation) {
+ Job *k, *delete;
+
+ /* If the marker is NULL we have been here already and
+ * decided the job was loop-free from here. Hence
+ * shortcut things and return right-away. */
+ if (!j->marker)
+ return 0;
+
+ /* So, the marker is not NULL and we already have been
+ * here. We have a cycle. Let's try to break it. We go
+ * backwards in our path and try to find a suitable
+ * job to remove. We use the marker to find our way
+ * back, since smart how we are we stored our way back
+ * in there. */
+ log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
+
+ delete = NULL;
+ for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
+
+ log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
+
+ if (!delete &&
+ !k->installed &&
+ !unit_matters_to_anchor(k->unit, k)) {
+ /* Ok, we can drop this one, so let's
+ * do so. */
+ delete = k;
+ }
+
+ /* Check if this in fact was the beginning of
+ * the cycle */
+ if (k == j)
+ break;
+ }
+
+
+ if (delete) {
+ log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
+ transaction_delete_unit(m, delete->unit);
+ return -EAGAIN;
+ }
+
+ log_error("Unable to break cycle");
+
+ dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
+ return -ENOEXEC;
+ }
+
+ /* Make the marker point to where we come from, so that we can
+ * find our way backwards if we want to break a cycle. We use
+ * a special marker for the beginning: we point to
+ * ourselves. */
+ j->marker = from ? from : j;
+ j->generation = generation;
+
+ /* We assume that the the dependencies are bidirectional, and
+ * hence can ignore UNIT_AFTER */
+ SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
+ Job *o;
+
+ /* Is there a job for this unit? */
+ if (!(o = hashmap_get(m->transaction_jobs, u)))
+
+ /* Ok, there is no job for this in the
+ * transaction, but maybe there is already one
+ * running? */
+ if (!(o = u->job))
+ continue;
+
+ if ((r = transaction_verify_order_one(m, o, j, generation, e)) < 0)
+ return r;
+ }
+
+ /* Ok, let's backtrack, and remember that this entry is not on
+ * our path anymore. */
+ j->marker = NULL;
+
+ return 0;
+}
+
+static int transaction_verify_order(Manager *m, unsigned *generation, DBusError *e) {
+ Job *j;
+ int r;
+ Iterator i;
+ unsigned g;
+
+ assert(m);
+ assert(generation);
+
+ /* Check if the ordering graph is cyclic. If it is, try to fix
+ * that up by dropping one of the jobs. */
+
+ g = (*generation)++;
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i)
+ if ((r = transaction_verify_order_one(m, j, NULL, g, e)) < 0)
+ return r;
+
+ return 0;
+}
+
+static void transaction_collect_garbage(Manager *m) {
+ bool again;
+
+ assert(m);
+
+ /* Drop jobs that are not required by any other job */
+
+ do {
+ Iterator i;
+ Job *j;
+
+ again = false;
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ if (j->object_list) {
+ /* log_debug("Keeping job %s/%s because of %s/%s", */
+ /* j->unit->id, job_type_to_string(j->type), */
+ /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
+ /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
+ continue;
+ }
+
+ /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
+ transaction_delete_job(m, j, true);
+ again = true;
+ break;
+ }
+
+ } while (again);
+}
+
+static int transaction_is_destructive(Manager *m, DBusError *e) {
+ Iterator i;
+ Job *j;
+
+ assert(m);
+
+ /* Checks whether applying this transaction means that
+ * existing jobs would be replaced */
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+
+ /* Assume merged */
+ assert(!j->transaction_prev);
+ assert(!j->transaction_next);
+
+ if (j->unit->job &&
+ j->unit->job != j &&
+ !job_type_is_superset(j->type, j->unit->job->type)) {
+
+ dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
+ return -EEXIST;
+ }
+ }
+
+ return 0;
+}
+
+static void transaction_minimize_impact(Manager *m) {
+ bool again;
+ assert(m);
+
+ /* Drops all unnecessary jobs that reverse already active jobs
+ * or that stop a running service. */
+
+ do {
+ Job *j;
+ Iterator i;
+
+ again = false;
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ LIST_FOREACH(transaction, j, j) {
+ bool stops_running_service, changes_existing_job;
+
+ /* If it matters, we shouldn't drop it */
+ if (j->matters_to_anchor)
+ continue;
+
+ /* Would this stop a running service?
+ * Would this change an existing job?
+ * If so, let's drop this entry */
+
+ stops_running_service =
+ j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
+
+ changes_existing_job =
+ j->unit->job &&
+ job_type_is_conflicting(j->type, j->unit->job->type);
+
+ if (!stops_running_service && !changes_existing_job)
+ continue;
+
+ if (stops_running_service)
+ log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
+
+ if (changes_existing_job)
+ log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
+
+ /* Ok, let's get rid of this */
+ log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
+
+ transaction_delete_job(m, j, true);
+ again = true;
+ break;
+ }
+
+ if (again)
+ break;
+ }
+
+ } while (again);
+}
+
+static int transaction_apply(Manager *m, JobMode mode) {
+ Iterator i;
+ Job *j;
+ int r;
+
+ /* Moves the transaction jobs to the set of active jobs */
+
+ if (mode == JOB_ISOLATE) {
+
+ /* When isolating first kill all installed jobs which
+ * aren't part of the new transaction */
+ rescan:
+ HASHMAP_FOREACH(j, m->jobs, i) {
+ assert(j->installed);
+
+ if (hashmap_get(m->transaction_jobs, j->unit))
+ continue;
+
+ /* 'j' itself is safe to remove, but if other jobs
+ are invalidated recursively, our iterator may become
+ invalid and we need to start over. */
+ if (job_finish_and_invalidate(j, JOB_CANCELED) > 0)
+ goto rescan;
+ }
+ }
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ /* Assume merged */
+ assert(!j->transaction_prev);
+ assert(!j->transaction_next);
+
+ if (j->installed)
+ continue;
+
+ if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0)
+ goto rollback;
+ }
+
+ while ((j = hashmap_steal_first(m->transaction_jobs))) {
+ if (j->installed) {
+ /* log_debug("Skipping already installed job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); */
+ continue;
+ }
+
+ if (j->unit->job)
+ job_free(j->unit->job);
+
+ j->unit->job = j;
+ j->installed = true;
+ m->n_installed_jobs ++;
+
+ /* We're fully installed. Now let's free data we don't
+ * need anymore. */
+
+ assert(!j->transaction_next);
+ assert(!j->transaction_prev);
+
+ job_add_to_run_queue(j);
+ job_add_to_dbus_queue(j);
+ job_start_timer(j);
+
+ log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ }
+
+ /* As last step, kill all remaining job dependencies. */
+ transaction_clean_dependencies(m);
+
+ return 0;
+
+rollback:
+
+ HASHMAP_FOREACH(j, m->transaction_jobs, i) {
+ if (j->installed)
+ continue;
+
+ hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
+ }
+
+ return r;
+}
+
+static int transaction_activate(Manager *m, JobMode mode, DBusError *e) {
+ int r;
+ unsigned generation = 1;
+
+ assert(m);
+
+ /* This applies the changes recorded in transaction_jobs to
+ * the actual list of jobs, if possible. */
+
+ /* First step: figure out which jobs matter */
+ transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++);
+
+ /* Second step: Try not to stop any running services if
+ * we don't have to. Don't try to reverse running
+ * jobs if we don't have to. */
+ if (mode == JOB_FAIL)
+ transaction_minimize_impact(m);
+
+ /* Third step: Drop redundant jobs */
+ transaction_drop_redundant(m);
+
+ for (;;) {
+ /* Fourth step: Let's remove unneeded jobs that might
+ * be lurking. */
+ if (mode != JOB_ISOLATE)
+ transaction_collect_garbage(m);
+
+ /* Fifth step: verify order makes sense and correct
+ * cycles if necessary and possible */
+ if ((r = transaction_verify_order(m, &generation, e)) >= 0)
+ break;
+
+ if (r != -EAGAIN) {
+ log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
+ goto rollback;
+ }
+
+ /* Let's see if the resulting transaction ordering
+ * graph is still cyclic... */
+ }
+
+ for (;;) {
+ /* Sixth step: let's drop unmergeable entries if
+ * necessary and possible, merge entries we can
+ * merge */
+ if ((r = transaction_merge_jobs(m, e)) >= 0)
+ break;
+
+ if (r != -EAGAIN) {
+ log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
+ goto rollback;
+ }
+
+ /* Seventh step: an entry got dropped, let's garbage
+ * collect its dependencies. */
+ if (mode != JOB_ISOLATE)
+ transaction_collect_garbage(m);
+
+ /* Let's see if the resulting transaction still has
+ * unmergeable entries ... */
+ }
+
+ /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
+ transaction_drop_redundant(m);
+
+ /* Ninth step: check whether we can actually apply this */
+ if (mode == JOB_FAIL)
+ if ((r = transaction_is_destructive(m, e)) < 0) {
+ log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
+ goto rollback;
+ }
+
+ /* Tenth step: apply changes */
+ if ((r = transaction_apply(m, mode)) < 0) {
+ log_warning("Failed to apply transaction: %s", strerror(-r));
+ goto rollback;
+ }
+
+ assert(hashmap_isempty(m->transaction_jobs));
+ assert(!m->transaction_anchor);
+
+ return 0;
+
+rollback:
+ transaction_abort(m);
+ return r;
+}
+
+static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool override, bool *is_new) {
+ Job *j, *f;
+
+ assert(m);
+ assert(unit);
+
+ /* Looks for an existing prospective job and returns that. If
+ * it doesn't exist it is created and added to the prospective
+ * jobs list. */
+
+ f = hashmap_get(m->transaction_jobs, unit);
+
+ LIST_FOREACH(transaction, j, f) {
+ assert(j->unit == unit);
+
+ if (j->type == type) {
+ if (is_new)
+ *is_new = false;
+ return j;
+ }
+ }
+
+ if (unit->job && unit->job->type == type)
+ j = unit->job;
+ else if (!(j = job_new(m, type, unit)))
+ return NULL;
+
+ j->generation = 0;
+ j->marker = NULL;
+ j->matters_to_anchor = false;
+ j->override = override;
+
+ LIST_PREPEND(Job, transaction, f, j);
+
+ if (hashmap_replace(m->transaction_jobs, unit, f) < 0) {
+ job_free(j);
+ return NULL;
+ }
+
+ if (is_new)
+ *is_new = true;
+
+ /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
+
+ return j;
+}
+
+void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
+ assert(m);
+ assert(j);
+
+ if (j->transaction_prev)
+ j->transaction_prev->transaction_next = j->transaction_next;
+ else if (j->transaction_next)
+ hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next);
+ else
+ hashmap_remove_value(m->transaction_jobs, j->unit, j);
+
+ if (j->transaction_next)
+ j->transaction_next->transaction_prev = j->transaction_prev;
+
+ j->transaction_prev = j->transaction_next = NULL;
+
+ while (j->subject_list)
+ job_dependency_free(j->subject_list);
+
+ while (j->object_list) {
+ Job *other = j->object_list->matters ? j->object_list->subject : NULL;
+
+ job_dependency_free(j->object_list);
+
+ if (other && delete_dependencies) {
+ log_debug("Deleting job %s/%s as dependency of job %s/%s",
+ other->unit->id, job_type_to_string(other->type),
+ j->unit->id, job_type_to_string(j->type));
+ transaction_delete_job(m, other, delete_dependencies);
+ }
+ }
+}
+
+static int transaction_add_job_and_dependencies(
+ Manager *m,
+ JobType type,
+ Unit *unit,
+ Job *by,
+ bool matters,
+ bool override,
+ bool conflicts,
+ bool ignore_requirements,
+ bool ignore_order,
+ DBusError *e,
+ Job **_ret) {
+ Job *ret;
+ Iterator i;
+ Unit *dep;
+ int r;
+ bool is_new;
+
+ assert(m);
+ assert(type < _JOB_TYPE_MAX);
+ assert(unit);
+
+ /* log_debug("Pulling in %s/%s from %s/%s", */
+ /* unit->id, job_type_to_string(type), */
+ /* by ? by->unit->id : "NA", */
+ /* by ? job_type_to_string(by->type) : "NA"); */
+
+ if (unit->load_state != UNIT_LOADED &&
+ unit->load_state != UNIT_ERROR &&
+ unit->load_state != UNIT_MASKED) {
+ dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
+ return -EINVAL;
+ }
+
+ if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
+ dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
+ "Unit %s failed to load: %s. "
+ "See system logs and 'systemctl status %s' for details.",
+ unit->id,
+ strerror(-unit->load_error),
+ unit->id);
+ return -EINVAL;
+ }
+
+ if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
+ dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
+ return -EINVAL;
+ }
+
+ if (!unit_job_is_applicable(unit, type)) {
+ dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->id);
+ return -EBADR;
+ }
+
+ /* First add the job. */
+ if (!(ret = transaction_add_one_job(m, type, unit, override, &is_new)))
+ return -ENOMEM;
+
+ ret->ignore_order = ret->ignore_order || ignore_order;
+
+ /* Then, add a link to the job. */
+ if (!job_dependency_new(by, ret, matters, conflicts))
+ return -ENOMEM;
+
+ if (is_new && !ignore_requirements) {
+ Set *following;
+
+ /* If we are following some other unit, make sure we
+ * add all dependencies of everybody following. */
+ if (unit_following_set(ret->unit, &following) > 0) {
+ SET_FOREACH(dep, following, i)
+ if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ set_free(following);
+ }
+
+ /* Finally, recursively add in all dependencies. */
+ if (type == JOB_START || type == JOB_RELOAD_OR_START) {
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_BIND_TO], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
+
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, false, ignore_order, e, NULL)) < 0) {
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
+
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e, NULL)) < 0) {
+
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i)
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
+ log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ }
+
+ if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i)
+ if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
+
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i)
+ if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
+
+ if (r != -EBADR)
+ goto fail;
+
+ if (e)
+ dbus_error_free(e);
+ }
+ }
+
+ if (type == JOB_RELOAD || type == JOB_RELOAD_OR_START) {
+
+ SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATE_RELOAD_TO], i) {
+ r = transaction_add_job_and_dependencies(m, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e, NULL);
+
+ if (r < 0) {
+ log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+
+ if (e)
+ dbus_error_free(e);
+ }
+ }
+ }
+
+ /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
+ }
+
+ if (_ret)
+ *_ret = ret;
+
+ return 0;
+
+fail:
+ return r;
+}
+
+static int transaction_add_isolate_jobs(Manager *m) {
+ Iterator i;
+ Unit *u;
+ char *k;
+ int r;
+
+ assert(m);
+
+ HASHMAP_FOREACH_KEY(u, k, m->units, i) {
+
+ /* ignore aliases */
+ if (u->id != k)
+ continue;
+
+ if (u->ignore_on_isolate)
+ continue;
+
+ /* No need to stop inactive jobs */
+ if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
+ continue;
+
+ /* Is there already something listed for this? */
+ if (hashmap_get(m->transaction_jobs, u))
+ continue;
+
+ if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, false, false, NULL, NULL)) < 0)
+ log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
+ }
+
+ return 0;
+}
+
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, DBusError *e, Job **_ret) {
+ int r;
+ Job *ret;
+
+ 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));
+
+ if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false,
+ mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
+ mode == JOB_IGNORE_DEPENDENCIES, e, &ret)) < 0) {
+ transaction_abort(m);
+ return r;
+ }
+
+ if (mode == JOB_ISOLATE)
+ if ((r = transaction_add_isolate_jobs(m)) < 0) {
+ transaction_abort(m);
+ return r;
+ }
+
+ if ((r = transaction_activate(m, mode, e)) < 0)
+ return r;
+
+ log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) ret->id);
+
+ if (_ret)
+ *_ret = ret;
+
+ return 0;
+}
+
+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 = file_name_from_path(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);
+
+ transaction_abort(m);
+
+ while ((j = hashmap_first(m->jobs)))
+ job_finish_and_invalidate(j, JOB_CANCELED);
+}
+
+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;
+ int wait_msec = -1;
+
+ 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;
+
+ /* 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;
+ }
+
+ while (m->exit_code == MANAGER_RUNNING) {
+ struct epoll_event event;
+ int n;
+
+ if (wait_msec >= 0)
+ 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;
+
+ 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_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
+ char *n;
+ Unit *u;
+
+ assert(m);
+ assert(s);
+ assert(_u);
+
+ if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
+ return -EINVAL;
+
+ if (!(n = bus_path_unescape(s+31)))
+ return -ENOMEM;
+
+ u = manager_get_unit(m, n);
+ free(n);
+
+ if (!u)
+ return -ENOENT;
+
+ *_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) {
+ log_warning("Failed to send audit message: %m");
+
+ if (errno == EPERM) {
+ /* We aren't allowed to send audit messages?
+ * Then let's not retry again, to avoid
+ * spamming the user with the same and same
+ * messages over and over. */
+
+ audit_close(m->audit_fd);
+ m->audit_fd = -1;
+ }
+ }
+
+ 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 = 0, initrd_usec = 0, userspace_usec = 0, total_usec = 0;
+
+ assert(m);
+
+ if (dual_timestamp_is_set(&m->finish_timestamp))
+ return;
+
+ if (hashmap_size(m->jobs) > 0)
+ 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);
+
+ if (!(l = strv_copy(controllers)))
+ return -ENOMEM;
+
+ strv_free(m->default_controllers);
+ m->default_controllers = l;
+
+ 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);
diff --git a/src/core/manager.h b/src/core/manager.h
new file mode 100644
index 0000000..9ecc926
--- /dev/null
+++ b/src/core/manager.h
@@ -0,0 +1,304 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foomanagerhfoo
+#define foomanagerhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <dbus/dbus.h>
+
+#include "fdset.h"
+
+/* Enforce upper limit how many names we allow */
+#define MANAGER_MAX_NAMES 131072 /* 128K */
+
+typedef struct Manager Manager;
+typedef enum WatchType WatchType;
+typedef struct Watch Watch;
+
+typedef enum ManagerExitCode {
+ MANAGER_RUNNING,
+ MANAGER_EXIT,
+ MANAGER_RELOAD,
+ MANAGER_REEXECUTE,
+ MANAGER_REBOOT,
+ MANAGER_POWEROFF,
+ MANAGER_HALT,
+ MANAGER_KEXEC,
+ _MANAGER_EXIT_CODE_MAX,
+ _MANAGER_EXIT_CODE_INVALID = -1
+} ManagerExitCode;
+
+typedef enum ManagerRunningAs {
+ MANAGER_SYSTEM,
+ MANAGER_USER,
+ _MANAGER_RUNNING_AS_MAX,
+ _MANAGER_RUNNING_AS_INVALID = -1
+} ManagerRunningAs;
+
+enum WatchType {
+ WATCH_INVALID,
+ WATCH_SIGNAL,
+ WATCH_NOTIFY,
+ WATCH_FD,
+ WATCH_UNIT_TIMER,
+ WATCH_JOB_TIMER,
+ WATCH_MOUNT,
+ WATCH_SWAP,
+ WATCH_UDEV,
+ WATCH_DBUS_WATCH,
+ WATCH_DBUS_TIMEOUT
+};
+
+struct Watch {
+ int fd;
+ WatchType type;
+ union {
+ struct Unit *unit;
+ struct Job *job;
+ DBusWatch *bus_watch;
+ DBusTimeout *bus_timeout;
+ } data;
+ bool fd_is_dupped:1;
+ bool socket_accept:1;
+};
+
+#include "unit.h"
+#include "job.h"
+#include "hashmap.h"
+#include "list.h"
+#include "set.h"
+#include "dbus.h"
+#include "path-lookup.h"
+
+struct Manager {
+ /* Note that the set of units we know of is allowed to be
+ * inconsistent. However the subset of it that is loaded may
+ * not, and the list of jobs may neither. */
+
+ /* Active jobs and units */
+ Hashmap *units; /* name string => Unit object n:1 */
+ Hashmap *jobs; /* job id => Job object 1:1 */
+
+ /* To make it easy to iterate through the units of a specific
+ * type we maintain a per type linked list */
+ LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]);
+
+ /* Units that need to be loaded */
+ LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */
+
+ /* Jobs that need to be run */
+ LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
+
+ /* Units and jobs that have not yet been announced via
+ * D-Bus. When something about a job changes it is added here
+ * if it is not in there yet. This allows easy coalescing of
+ * D-Bus change signals. */
+ LIST_HEAD(Unit, dbus_unit_queue);
+ LIST_HEAD(Job, dbus_job_queue);
+
+ /* Units to remove */
+ LIST_HEAD(Unit, cleanup_queue);
+
+ /* Units to check when doing GC */
+ LIST_HEAD(Unit, gc_queue);
+
+ /* Jobs to be added */
+ Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
+ JobDependency *transaction_anchor;
+
+ Hashmap *watch_pids; /* pid => Unit object n:1 */
+
+ char *notify_socket;
+
+ Watch notify_watch;
+ Watch signal_watch;
+
+ int epoll_fd;
+
+ unsigned n_snapshots;
+
+ LookupPaths lookup_paths;
+ Set *unit_path_cache;
+
+ char **environment;
+ char **default_controllers;
+
+ usec_t runtime_watchdog;
+ usec_t shutdown_watchdog;
+
+ dual_timestamp initrd_timestamp;
+ dual_timestamp startup_timestamp;
+ dual_timestamp finish_timestamp;
+
+ char *generator_unit_path;
+
+ /* Data specific to the device subsystem */
+ struct udev* udev;
+ struct udev_monitor* udev_monitor;
+ Watch udev_watch;
+ Hashmap *devices_by_sysfs;
+
+ /* Data specific to the mount subsystem */
+ FILE *proc_self_mountinfo;
+ Watch mount_watch;
+
+ /* Data specific to the swap filesystem */
+ FILE *proc_swaps;
+ Hashmap *swaps_by_proc_swaps;
+ bool request_reload;
+ Watch swap_watch;
+
+ /* Data specific to the D-Bus subsystem */
+ DBusConnection *api_bus, *system_bus;
+ DBusServer *private_bus;
+ Set *bus_connections, *bus_connections_for_dispatch;
+
+ DBusMessage *queued_message; /* This is used during reloading:
+ * before the reload we queue the
+ * reply message here, and
+ * afterwards we send it */
+ DBusConnection *queued_message_connection; /* The connection to send the queued message on */
+
+ Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
+ int32_t name_data_slot;
+ int32_t conn_data_slot;
+ int32_t subscribed_data_slot;
+
+ uint32_t current_job_id;
+ uint32_t default_unit_job_id;
+
+ /* Data specific to the Automount subsystem */
+ int dev_autofs_fd;
+
+ /* Data specific to the cgroup subsystem */
+ Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
+ char *cgroup_hierarchy;
+
+ usec_t gc_queue_timestamp;
+ int gc_marker;
+ unsigned n_in_gc_queue;
+
+ /* Make sure the user cannot accidentally unmount our cgroup
+ * file system */
+ int pin_cgroupfs_fd;
+
+ /* Audit fd */
+#ifdef HAVE_AUDIT
+ int audit_fd;
+#endif
+
+ /* Flags */
+ ManagerRunningAs running_as;
+ ManagerExitCode exit_code:5;
+
+ bool dispatching_load_queue:1;
+ bool dispatching_run_queue:1;
+ bool dispatching_dbus_queue:1;
+
+ bool taint_usr:1;
+
+ bool show_status;
+ bool confirm_spawn;
+#ifdef HAVE_SYSV_COMPAT
+ bool sysv_console;
+#endif
+ bool mount_auto;
+ bool swap_auto;
+
+ ExecOutput default_std_output, default_std_error;
+
+ /* non-zero if we are reloading or reexecuting, */
+ int n_reloading;
+
+ unsigned n_installed_jobs;
+ unsigned n_failed_jobs;
+};
+
+int manager_new(ManagerRunningAs running_as, Manager **m);
+void manager_free(Manager *m);
+
+int manager_enumerate(Manager *m);
+int manager_coldplug(Manager *m);
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
+
+Job *manager_get_job(Manager *m, uint32_t id);
+Unit *manager_get_unit(Manager *m, const char *name);
+
+int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u);
+int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
+
+int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret);
+int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret);
+
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, DBusError *e, Job **_ret);
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, DBusError *e, Job **_ret);
+
+void manager_dump_units(Manager *s, FILE *f, const char *prefix);
+void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
+
+void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies);
+
+void manager_clear_jobs(Manager *m);
+
+unsigned manager_dispatch_load_queue(Manager *m);
+unsigned manager_dispatch_run_queue(Manager *m);
+unsigned manager_dispatch_dbus_queue(Manager *m);
+
+int manager_set_default_controllers(Manager *m, char **controllers);
+
+int manager_loop(Manager *m);
+
+void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
+void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid);
+
+int manager_open_serialization(Manager *m, FILE **_f);
+
+int manager_serialize(Manager *m, FILE *f, FDSet *fds);
+int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
+
+int manager_reload(Manager *m);
+
+bool manager_is_booting_or_shutting_down(Manager *m);
+
+void manager_reset_failed(Manager *m);
+
+void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
+void manager_send_unit_plymouth(Manager *m, Unit *u);
+
+bool manager_unit_pending_inactive(Manager *m, const char *name);
+
+void manager_check_finished(Manager *m);
+
+void manager_run_generators(Manager *m);
+void manager_undo_generators(Manager *m);
+
+void manager_recheck_journal(Manager *m);
+
+void manager_set_show_status(Manager *m, bool b);
+bool manager_get_show_status(Manager *m);
+
+const char *manager_running_as_to_string(ManagerRunningAs i);
+ManagerRunningAs manager_running_as_from_string(const char *s);
+
+#endif
diff --git a/src/core/mount.c b/src/core/mount.c
new file mode 100644
index 0000000..7dbeaf9
--- /dev/null
+++ b/src/core/mount.c
@@ -0,0 +1,1930 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stdio.h>
+#include <mntent.h>
+#include <sys/epoll.h>
+#include <signal.h>
+
+#include "unit.h"
+#include "mount.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "strv.h"
+#include "mkdir.h"
+#include "mount-setup.h"
+#include "unit-name.h"
+#include "dbus-mount.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "exit-status.h"
+#include "def.h"
+
+static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
+ [MOUNT_DEAD] = UNIT_INACTIVE,
+ [MOUNT_MOUNTING] = UNIT_ACTIVATING,
+ [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE,
+ [MOUNT_MOUNTED] = UNIT_ACTIVE,
+ [MOUNT_REMOUNTING] = UNIT_RELOADING,
+ [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
+ [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING,
+ [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING,
+ [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING,
+ [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING,
+ [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING,
+ [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING,
+ [MOUNT_FAILED] = UNIT_FAILED
+};
+
+static void mount_init(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ m->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ m->directory_mode = 0755;
+
+ exec_context_init(&m->exec_context);
+
+ /* The stdio/kmsg bridge socket is on /, in order to avoid a
+ * dep loop, don't use kmsg logging for -.mount */
+ if (!unit_has_name(u, "-.mount")) {
+ m->exec_context.std_output = u->manager->default_std_output;
+ m->exec_context.std_error = u->manager->default_std_error;
+ }
+
+ /* We need to make sure that /bin/mount is always called in
+ * the same process group as us, so that the autofs kernel
+ * side doesn't send us another mount request while we are
+ * already trying to comply its last one. */
+ m->exec_context.same_pgrp = true;
+
+ m->timer_watch.type = WATCH_INVALID;
+
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
+
+ UNIT(m)->ignore_on_isolate = true;
+}
+
+static void mount_unwatch_control_pid(Mount *m) {
+ assert(m);
+
+ if (m->control_pid <= 0)
+ return;
+
+ unit_unwatch_pid(UNIT(m), m->control_pid);
+ m->control_pid = 0;
+}
+
+static void mount_parameters_done(MountParameters *p) {
+ assert(p);
+
+ free(p->what);
+ free(p->options);
+ free(p->fstype);
+
+ p->what = p->options = p->fstype = NULL;
+}
+
+static void mount_done(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ free(m->where);
+ m->where = NULL;
+
+ mount_parameters_done(&m->parameters_etc_fstab);
+ mount_parameters_done(&m->parameters_proc_self_mountinfo);
+ mount_parameters_done(&m->parameters_fragment);
+
+ exec_context_done(&m->exec_context);
+ exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
+ m->control_command = NULL;
+
+ mount_unwatch_control_pid(m);
+
+ unit_unwatch_timer(u, &m->timer_watch);
+}
+
+static MountParameters* get_mount_parameters_configured(Mount *m) {
+ assert(m);
+
+ if (m->from_fragment)
+ return &m->parameters_fragment;
+ else if (m->from_etc_fstab)
+ return &m->parameters_etc_fstab;
+
+ return NULL;
+}
+
+static MountParameters* get_mount_parameters(Mount *m) {
+ assert(m);
+
+ if (m->from_proc_self_mountinfo)
+ return &m->parameters_proc_self_mountinfo;
+
+ return get_mount_parameters_configured(m);
+}
+
+static int mount_add_mount_links(Mount *m) {
+ Unit *other;
+ int r;
+ MountParameters *pm;
+
+ assert(m);
+
+ pm = get_mount_parameters_configured(m);
+
+ /* Adds in links to other mount points that might lie below or
+ * above us in the hierarchy */
+
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) {
+ Mount *n = MOUNT(other);
+ MountParameters *pn;
+
+ if (n == m)
+ continue;
+
+ if (UNIT(n)->load_state != UNIT_LOADED)
+ continue;
+
+ pn = get_mount_parameters_configured(n);
+
+ if (path_startswith(m->where, n->where)) {
+
+ if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
+ return r;
+
+ if (pn)
+ if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
+ return r;
+
+ } else if (path_startswith(n->where, m->where)) {
+
+ if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
+ return r;
+
+ if (pm)
+ if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+
+ } else if (pm && pm->what && path_startswith(pm->what, n->where)) {
+
+ if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
+ return r;
+
+ if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
+ return r;
+
+ } else if (pn && pn->what && path_startswith(pn->what, m->where)) {
+
+ if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
+ return r;
+
+ if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int mount_add_swap_links(Mount *m) {
+ Unit *other;
+ int r;
+
+ assert(m);
+
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP])
+ if ((r = swap_add_one_mount_link(SWAP(other), m)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int mount_add_path_links(Mount *m) {
+ Unit *other;
+ int r;
+
+ assert(m);
+
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH])
+ if ((r = path_add_one_mount_link(PATH(other), m)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int mount_add_automount_links(Mount *m) {
+ Unit *other;
+ int r;
+
+ assert(m);
+
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT])
+ if ((r = automount_add_one_mount_link(AUTOMOUNT(other), m)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int mount_add_socket_links(Mount *m) {
+ Unit *other;
+ int r;
+
+ assert(m);
+
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET])
+ if ((r = socket_add_one_mount_link(SOCKET(other), m)) < 0)
+ return r;
+
+ return 0;
+}
+
+static char* mount_test_option(const char *haystack, const char *needle) {
+ struct mntent me;
+
+ assert(needle);
+
+ /* Like glibc's hasmntopt(), but works on a string, not a
+ * struct mntent */
+
+ if (!haystack)
+ return NULL;
+
+ zero(me);
+ me.mnt_opts = (char*) haystack;
+
+ return hasmntopt(&me, needle);
+}
+
+static bool mount_is_network(MountParameters *p) {
+ assert(p);
+
+ if (mount_test_option(p->options, "_netdev"))
+ return true;
+
+ if (p->fstype && fstype_is_network(p->fstype))
+ return true;
+
+ return false;
+}
+
+static bool mount_is_bind(MountParameters *p) {
+ assert(p);
+
+ if (mount_test_option(p->options, "bind"))
+ return true;
+
+ if (p->fstype && streq(p->fstype, "bind"))
+ return true;
+
+ return false;
+}
+
+static bool needs_quota(MountParameters *p) {
+ assert(p);
+
+ if (mount_is_network(p))
+ return false;
+
+ if (mount_is_bind(p))
+ return false;
+
+ return mount_test_option(p->options, "usrquota") ||
+ mount_test_option(p->options, "grpquota") ||
+ mount_test_option(p->options, "quota") ||
+ mount_test_option(p->options, "usrjquota") ||
+ mount_test_option(p->options, "grpjquota");
+}
+
+static int mount_add_fstab_links(Mount *m) {
+ const char *target, *after, *tu_wants = NULL;
+ MountParameters *p;
+ Unit *tu;
+ int r;
+ bool noauto, nofail, handle, automount;
+
+ assert(m);
+
+ if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ return 0;
+
+ if (!(p = get_mount_parameters_configured(m)))
+ return 0;
+
+ if (p != &m->parameters_etc_fstab)
+ return 0;
+
+ noauto = !!mount_test_option(p->options, "noauto");
+ nofail = !!mount_test_option(p->options, "nofail");
+ automount =
+ mount_test_option(p->options, "comment=systemd.automount") ||
+ mount_test_option(p->options, "x-systemd-automount");
+ handle =
+ automount ||
+ mount_test_option(p->options, "comment=systemd.mount") ||
+ mount_test_option(p->options, "x-systemd-mount") ||
+ UNIT(m)->manager->mount_auto;
+
+ if (mount_is_network(p)) {
+ target = SPECIAL_REMOTE_FS_TARGET;
+ after = tu_wants = SPECIAL_REMOTE_FS_PRE_TARGET;
+ } else {
+ target = SPECIAL_LOCAL_FS_TARGET;
+ after = SPECIAL_LOCAL_FS_PRE_TARGET;
+ }
+
+ r = manager_load_unit(UNIT(m)->manager, target, NULL, NULL, &tu);
+ if (r < 0)
+ return r;
+
+ if (tu_wants) {
+ r = unit_add_dependency_by_name(tu, UNIT_WANTS, tu_wants, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (after) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (automount) {
+ Unit *am;
+
+ if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0)
+ return r;
+
+ /* If auto is configured as well also pull in the
+ * mount right-away, but don't rely on it. */
+ if (!noauto) /* automount + auto */
+ if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true)) < 0)
+ return r;
+
+ /* Install automount unit */
+ if (!nofail) /* automount + fail */
+ return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, am, true);
+ else /* automount + nofail */
+ return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_WANTS, am, true);
+
+ } else if (handle && !noauto) {
+
+ /* Automatically add mount points that aren't natively
+ * configured to local-fs.target */
+
+ if (!nofail) /* auto + fail */
+ return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+ else /* auto + nofail */
+ return unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true);
+ }
+
+ return 0;
+}
+
+static int mount_add_device_links(Mount *m) {
+ MountParameters *p;
+ int r;
+
+ assert(m);
+
+ if (!(p = get_mount_parameters_configured(m)))
+ return 0;
+
+ if (!p->what)
+ return 0;
+
+ if (!mount_is_bind(p) &&
+ !path_equal(m->where, "/") &&
+ p == &m->parameters_etc_fstab) {
+ bool nofail, noauto;
+
+ noauto = !!mount_test_option(p->options, "noauto");
+ nofail = !!mount_test_option(p->options, "nofail");
+
+ if ((r = unit_add_node_link(UNIT(m), p->what,
+ !noauto && nofail &&
+ UNIT(m)->manager->running_as == MANAGER_SYSTEM)) < 0)
+ return r;
+ }
+
+ if (p->passno > 0 &&
+ !mount_is_bind(p) &&
+ UNIT(m)->manager->running_as == MANAGER_SYSTEM &&
+ !path_equal(m->where, "/")) {
+ char *name;
+ Unit *fsck;
+ /* Let's add in the fsck service */
+
+ /* aka SPECIAL_FSCK_SERVICE */
+ if (!(name = unit_name_from_path_instance("fsck", p->what, ".service")))
+ return -ENOMEM;
+
+ if ((r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck)) < 0) {
+ log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
+ free(name);
+ return r;
+ }
+
+ free(name);
+
+ SERVICE(fsck)->fsck_passno = p->passno;
+
+ if ((r = unit_add_two_dependencies(UNIT(m), UNIT_AFTER, UNIT_REQUIRES, fsck, true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int mount_add_default_dependencies(Mount *m) {
+ int r;
+ MountParameters *p;
+
+ assert(m);
+
+ if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ return 0;
+
+ p = get_mount_parameters_configured(m);
+ if (p && needs_quota(p)) {
+ if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true)) < 0 ||
+ (r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true)) < 0)
+ return r;
+ }
+
+ if (!path_equal(m->where, "/"))
+ if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int mount_fix_timeouts(Mount *m) {
+ MountParameters *p;
+ const char *timeout = NULL;
+ Unit *other;
+ Iterator i;
+ usec_t u;
+ char *t;
+ int r;
+
+ assert(m);
+
+ if (!(p = get_mount_parameters_configured(m)))
+ return 0;
+
+ /* Allow configuration how long we wait for a device that
+ * backs a mount point to show up. This is useful to support
+ * endless device timeouts for devices that show up only after
+ * user input, like crypto devices. */
+
+ if ((timeout = mount_test_option(p->options, "comment=systemd.device-timeout")))
+ timeout += 31;
+ else if ((timeout = mount_test_option(p->options, "x-systemd-device-timeout")))
+ timeout += 25;
+ else
+ return 0;
+
+ t = strndup(timeout, strcspn(timeout, ",;" WHITESPACE));
+ if (!t)
+ return -ENOMEM;
+
+ r = parse_usec(t, &u);
+ free(t);
+
+ if (r < 0) {
+ log_warning("Failed to parse timeout for %s, ignoring: %s", m->where, timeout);
+ return r;
+ }
+
+ SET_FOREACH(other, UNIT(m)->dependencies[UNIT_AFTER], i) {
+ if (other->type != UNIT_DEVICE)
+ continue;
+
+ other->job_timeout = u;
+ }
+
+ return 0;
+}
+
+static int mount_verify(Mount *m) {
+ bool b;
+ char *e;
+ assert(m);
+
+ if (UNIT(m)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!m->from_etc_fstab && !m->from_fragment && !m->from_proc_self_mountinfo)
+ return -ENOENT;
+
+ if (!(e = unit_name_from_path(m->where, ".mount")))
+ return -ENOMEM;
+
+ b = unit_has_name(UNIT(m), e);
+ free(e);
+
+ if (!b) {
+ log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(m)->id);
+ return -EINVAL;
+ }
+
+ if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) {
+ log_error("Cannot create mount unit for API file system %s. Refusing.", m->where);
+ return -EINVAL;
+ }
+
+ if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
+ log_error("%s's What setting is missing. Refusing.", UNIT(m)->id);
+ return -EBADMSG;
+ }
+
+ if (m->exec_context.pam_name && m->exec_context.kill_mode != KILL_CONTROL_GROUP) {
+ log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(m)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mount_load(Unit *u) {
+ Mount *m = MOUNT(u);
+ int r;
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
+ return r;
+
+ /* This is a new unit? Then let's add in some extras */
+ if (u->load_state == UNIT_LOADED) {
+ if ((r = unit_add_exec_dependencies(u, &m->exec_context)) < 0)
+ return r;
+
+ if (UNIT(m)->fragment_path)
+ m->from_fragment = true;
+ else if (m->from_etc_fstab)
+ /* We always add several default dependencies to fstab mounts,
+ * but we do not want the implicit complementing of Wants= with After=
+ * in the target unit that this mount unit will be hooked into. */
+ UNIT(m)->default_dependencies = false;
+
+ if (!m->where)
+ if (!(m->where = unit_name_to_path(u->id)))
+ return -ENOMEM;
+
+ path_kill_slashes(m->where);
+
+ if (!UNIT(m)->description)
+ if ((r = unit_set_description(u, m->where)) < 0)
+ return r;
+
+ if ((r = mount_add_device_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_mount_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_socket_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_swap_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_path_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_automount_links(m)) < 0)
+ return r;
+
+ if ((r = mount_add_fstab_links(m)) < 0)
+ return r;
+
+ if (UNIT(m)->default_dependencies || m->from_etc_fstab)
+ if ((r = mount_add_default_dependencies(m)) < 0)
+ return r;
+
+ if ((r = unit_add_default_cgroups(u)) < 0)
+ return r;
+
+ mount_fix_timeouts(m);
+ }
+
+ return mount_verify(m);
+}
+
+static int mount_notify_automount(Mount *m, int status) {
+ Unit *p;
+ int r;
+ Iterator i;
+
+ assert(m);
+
+ SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
+ if (p->type == UNIT_AUTOMOUNT) {
+ r = automount_send_ready(AUTOMOUNT(p), status);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static void mount_set_state(Mount *m, MountState state) {
+ MountState old_state;
+ assert(m);
+
+ old_state = m->state;
+ m->state = state;
+
+ if (state != MOUNT_MOUNTING &&
+ state != MOUNT_MOUNTING_DONE &&
+ state != MOUNT_REMOUNTING &&
+ state != MOUNT_UNMOUNTING &&
+ state != MOUNT_MOUNTING_SIGTERM &&
+ state != MOUNT_MOUNTING_SIGKILL &&
+ state != MOUNT_UNMOUNTING_SIGTERM &&
+ state != MOUNT_UNMOUNTING_SIGKILL &&
+ state != MOUNT_REMOUNTING_SIGTERM &&
+ state != MOUNT_REMOUNTING_SIGKILL) {
+ unit_unwatch_timer(UNIT(m), &m->timer_watch);
+ mount_unwatch_control_pid(m);
+ m->control_command = NULL;
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
+ }
+
+ if (state == MOUNT_MOUNTED ||
+ state == MOUNT_REMOUNTING)
+ mount_notify_automount(m, 0);
+ else if (state == MOUNT_DEAD ||
+ state == MOUNT_UNMOUNTING ||
+ state == MOUNT_MOUNTING_SIGTERM ||
+ state == MOUNT_MOUNTING_SIGKILL ||
+ state == MOUNT_REMOUNTING_SIGTERM ||
+ state == MOUNT_REMOUNTING_SIGKILL ||
+ state == MOUNT_UNMOUNTING_SIGTERM ||
+ state == MOUNT_UNMOUNTING_SIGKILL ||
+ state == MOUNT_FAILED)
+ mount_notify_automount(m, -ENODEV);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(m)->id,
+ mount_state_to_string(old_state),
+ mount_state_to_string(state));
+
+ unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
+ m->reload_result = MOUNT_SUCCESS;
+}
+
+static int mount_coldplug(Unit *u) {
+ Mount *m = MOUNT(u);
+ MountState new_state = MOUNT_DEAD;
+ int r;
+
+ assert(m);
+ assert(m->state == MOUNT_DEAD);
+
+ if (m->deserialized_state != m->state)
+ new_state = m->deserialized_state;
+ else if (m->from_proc_self_mountinfo)
+ new_state = MOUNT_MOUNTED;
+
+ if (new_state != m->state) {
+
+ if (new_state == MOUNT_MOUNTING ||
+ new_state == MOUNT_MOUNTING_DONE ||
+ new_state == MOUNT_REMOUNTING ||
+ new_state == MOUNT_UNMOUNTING ||
+ new_state == MOUNT_MOUNTING_SIGTERM ||
+ new_state == MOUNT_MOUNTING_SIGKILL ||
+ new_state == MOUNT_UNMOUNTING_SIGTERM ||
+ new_state == MOUNT_UNMOUNTING_SIGKILL ||
+ new_state == MOUNT_REMOUNTING_SIGTERM ||
+ new_state == MOUNT_REMOUNTING_SIGKILL) {
+
+ if (m->control_pid <= 0)
+ return -EBADMSG;
+
+ if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ return r;
+ }
+
+ mount_set_state(m, new_state);
+ }
+
+ return 0;
+}
+
+static void mount_dump(Unit *u, FILE *f, const char *prefix) {
+ Mount *m = MOUNT(u);
+ MountParameters *p;
+
+ assert(m);
+ assert(f);
+
+ p = get_mount_parameters(m);
+
+ fprintf(f,
+ "%sMount State: %s\n"
+ "%sResult: %s\n"
+ "%sWhere: %s\n"
+ "%sWhat: %s\n"
+ "%sFile System Type: %s\n"
+ "%sOptions: %s\n"
+ "%sFrom /etc/fstab: %s\n"
+ "%sFrom /proc/self/mountinfo: %s\n"
+ "%sFrom fragment: %s\n"
+ "%sDirectoryMode: %04o\n",
+ prefix, mount_state_to_string(m->state),
+ prefix, mount_result_to_string(m->result),
+ prefix, m->where,
+ prefix, strna(p->what),
+ prefix, strna(p->fstype),
+ prefix, strna(p->options),
+ prefix, yes_no(m->from_etc_fstab),
+ prefix, yes_no(m->from_proc_self_mountinfo),
+ prefix, yes_no(m->from_fragment),
+ prefix, m->directory_mode);
+
+ if (m->control_pid > 0)
+ fprintf(f,
+ "%sControl PID: %lu\n",
+ prefix, (unsigned long) m->control_pid);
+
+ exec_context_dump(&m->exec_context, f, prefix);
+}
+
+static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
+ pid_t pid;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(_pid);
+
+ if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ goto fail;
+
+ if ((r = exec_spawn(c,
+ NULL,
+ &m->exec_context,
+ NULL, 0,
+ UNIT(m)->manager->environment,
+ true,
+ true,
+ true,
+ UNIT(m)->manager->confirm_spawn,
+ UNIT(m)->cgroup_bondings,
+ UNIT(m)->cgroup_attributes,
+ &pid)) < 0)
+ goto fail;
+
+ if ((r = unit_watch_pid(UNIT(m), pid)) < 0)
+ /* FIXME: we need to do something here */
+ goto fail;
+
+ *_pid = pid;
+
+ return 0;
+
+fail:
+ unit_unwatch_timer(UNIT(m), &m->timer_watch);
+
+ return r;
+}
+
+static void mount_enter_dead(Mount *m, MountResult f) {
+ assert(m);
+
+ if (f != MOUNT_SUCCESS)
+ m->result = f;
+
+ mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
+}
+
+static void mount_enter_mounted(Mount *m, MountResult f) {
+ assert(m);
+
+ if (f != MOUNT_SUCCESS)
+ m->result = f;
+
+ mount_set_state(m, MOUNT_MOUNTED);
+}
+
+static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
+ int r;
+ Set *pid_set = NULL;
+ bool wait_for_exit = false;
+
+ assert(m);
+
+ if (f != MOUNT_SUCCESS)
+ m->result = f;
+
+ if (m->exec_context.kill_mode != KILL_NONE) {
+ int sig = (state == MOUNT_MOUNTING_SIGTERM ||
+ state == MOUNT_UNMOUNTING_SIGTERM ||
+ state == MOUNT_REMOUNTING_SIGTERM) ? m->exec_context.kill_signal : SIGKILL;
+
+ if (m->control_pid > 0) {
+ if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH)
+
+ log_warning("Failed to kill control process %li: %m", (long) m->control_pid);
+ else
+ wait_for_exit = true;
+ }
+
+ if (m->exec_context.kill_mode == KILL_CONTROL_GROUP) {
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (m->control_pid > 0)
+ if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0)
+ goto fail;
+
+ if ((r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, pid_set)) < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning("Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+
+ set_free(pid_set);
+ pid_set = NULL;
+ }
+ }
+
+ if (wait_for_exit) {
+ if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ goto fail;
+
+ mount_set_state(m, state);
+ } else if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
+ mount_enter_mounted(m, MOUNT_SUCCESS);
+ else
+ mount_enter_dead(m, MOUNT_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to kill processes: %s", UNIT(m)->id, strerror(-r));
+
+ if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
+ mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
+ else
+ mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
+
+ if (pid_set)
+ set_free(pid_set);
+}
+
+static void mount_enter_unmounting(Mount *m) {
+ int r;
+
+ assert(m);
+
+ m->control_command_id = MOUNT_EXEC_UNMOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
+
+ if ((r = exec_command_set(
+ m->control_command,
+ "/bin/umount",
+ m->where,
+ NULL)) < 0)
+ goto fail;
+
+ mount_unwatch_control_pid(m);
+
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+ goto fail;
+
+ mount_set_state(m, MOUNT_UNMOUNTING);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r));
+ mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
+}
+
+static void mount_enter_mounting(Mount *m) {
+ int r;
+ MountParameters *p;
+
+ assert(m);
+
+ m->control_command_id = MOUNT_EXEC_MOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
+
+ mkdir_p(m->where, m->directory_mode);
+
+ /* Create the source directory for bind-mounts if needed */
+ p = get_mount_parameters_configured(m);
+ if (p && mount_is_bind(p))
+ mkdir_p(p->what, m->directory_mode);
+
+ if (m->from_fragment)
+ r = exec_command_set(
+ m->control_command,
+ "/bin/mount",
+ m->parameters_fragment.what,
+ m->where,
+ "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
+ m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options,
+ NULL);
+ else if (m->from_etc_fstab)
+ r = exec_command_set(
+ m->control_command,
+ "/bin/mount",
+ m->where,
+ NULL);
+ else
+ r = -ENOENT;
+
+ if (r < 0)
+ goto fail;
+
+ mount_unwatch_control_pid(m);
+
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+ goto fail;
+
+ mount_set_state(m, MOUNT_MOUNTING);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r));
+ mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
+}
+
+static void mount_enter_mounting_done(Mount *m) {
+ assert(m);
+
+ mount_set_state(m, MOUNT_MOUNTING_DONE);
+}
+
+static void mount_enter_remounting(Mount *m) {
+ int r;
+
+ assert(m);
+
+ m->control_command_id = MOUNT_EXEC_REMOUNT;
+ m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
+
+ if (m->from_fragment) {
+ char *buf = NULL;
+ const char *o;
+
+ if (m->parameters_fragment.options) {
+ if (!(buf = strappend("remount,", m->parameters_fragment.options))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ o = buf;
+ } else
+ o = "remount";
+
+ r = exec_command_set(
+ m->control_command,
+ "/bin/mount",
+ m->parameters_fragment.what,
+ m->where,
+ "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
+ "-o", o,
+ NULL);
+
+ free(buf);
+ } else if (m->from_etc_fstab)
+ r = exec_command_set(
+ m->control_command,
+ "/bin/mount",
+ m->where,
+ "-o", "remount",
+ NULL);
+ else
+ r = -ENOENT;
+
+ if (r < 0)
+ goto fail;
+
+ mount_unwatch_control_pid(m);
+
+ if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+ goto fail;
+
+ mount_set_state(m, MOUNT_REMOUNTING);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r));
+ m->reload_result = MOUNT_FAILURE_RESOURCES;
+ mount_enter_mounted(m, MOUNT_SUCCESS);
+}
+
+static int mount_start(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ /* We cannot fulfill this request right now, try again later
+ * please! */
+ if (m->state == MOUNT_UNMOUNTING ||
+ m->state == MOUNT_UNMOUNTING_SIGTERM ||
+ m->state == MOUNT_UNMOUNTING_SIGKILL ||
+ m->state == MOUNT_MOUNTING_SIGTERM ||
+ m->state == MOUNT_MOUNTING_SIGKILL)
+ return -EAGAIN;
+
+ /* Already on it! */
+ if (m->state == MOUNT_MOUNTING)
+ return 0;
+
+ assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
+
+ m->result = MOUNT_SUCCESS;
+ m->reload_result = MOUNT_SUCCESS;
+
+ mount_enter_mounting(m);
+ return 0;
+}
+
+static int mount_stop(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ /* Already on it */
+ if (m->state == MOUNT_UNMOUNTING ||
+ m->state == MOUNT_UNMOUNTING_SIGKILL ||
+ m->state == MOUNT_UNMOUNTING_SIGTERM ||
+ m->state == MOUNT_MOUNTING_SIGTERM ||
+ m->state == MOUNT_MOUNTING_SIGKILL)
+ return 0;
+
+ assert(m->state == MOUNT_MOUNTING ||
+ m->state == MOUNT_MOUNTING_DONE ||
+ m->state == MOUNT_MOUNTED ||
+ m->state == MOUNT_REMOUNTING ||
+ m->state == MOUNT_REMOUNTING_SIGTERM ||
+ m->state == MOUNT_REMOUNTING_SIGKILL);
+
+ mount_enter_unmounting(m);
+ return 0;
+}
+
+static int mount_reload(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ if (m->state == MOUNT_MOUNTING_DONE)
+ return -EAGAIN;
+
+ assert(m->state == MOUNT_MOUNTED);
+
+ mount_enter_remounting(m);
+ return 0;
+}
+
+static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", mount_state_to_string(m->state));
+ unit_serialize_item(u, f, "result", mount_result_to_string(m->result));
+ unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result));
+
+ if (m->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) m->control_pid);
+
+ if (m->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
+
+ return 0;
+}
+
+static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Mount *m = MOUNT(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ MountState state;
+
+ if ((state = mount_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ m->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ MountResult f;
+
+ f = mount_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != MOUNT_SUCCESS)
+ m->result = f;
+
+ } else if (streq(key, "reload-result")) {
+ MountResult f;
+
+ f = mount_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse reload result value %s", value);
+ else if (f != MOUNT_SUCCESS)
+ m->reload_result = f;
+
+ } else if (streq(key, "control-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ m->control_pid = pid;
+ } else if (streq(key, "control-command")) {
+ MountExecCommand id;
+
+ if ((id = mount_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ m->control_command_id = id;
+ m->control_command = m->exec_command + id;
+ }
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState mount_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[MOUNT(u)->state];
+}
+
+static const char *mount_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return mount_state_to_string(MOUNT(u)->state);
+}
+
+static bool mount_check_gc(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ return m->from_etc_fstab || m->from_proc_self_mountinfo;
+}
+
+static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ Mount *m = MOUNT(u);
+ MountResult f;
+
+ assert(m);
+ assert(pid >= 0);
+
+ if (pid != m->control_pid)
+ return;
+
+ m->control_pid = 0;
+
+ if (is_clean_exit(code, status))
+ f = MOUNT_SUCCESS;
+ else if (code == CLD_EXITED)
+ f = MOUNT_FAILURE_EXIT_CODE;
+ else if (code == CLD_KILLED)
+ f = MOUNT_FAILURE_SIGNAL;
+ else if (code == CLD_DUMPED)
+ f = MOUNT_FAILURE_CORE_DUMP;
+ else
+ assert_not_reached("Unknown code");
+
+ if (f != MOUNT_SUCCESS)
+ m->result = f;
+
+ if (m->control_command) {
+ exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status);
+
+ m->control_command = NULL;
+ m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
+ }
+
+ log_full(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+
+ /* Note that mount(8) returning and the kernel sending us a
+ * mount table change event might happen out-of-order. If an
+ * operation succeed we assume the kernel will follow soon too
+ * and already change into the resulting state. If it fails
+ * we check if the kernel still knows about the mount. and
+ * change state accordingly. */
+
+ switch (m->state) {
+
+ case MOUNT_MOUNTING:
+ case MOUNT_MOUNTING_DONE:
+ case MOUNT_MOUNTING_SIGKILL:
+ case MOUNT_MOUNTING_SIGTERM:
+
+ if (f == MOUNT_SUCCESS)
+ mount_enter_mounted(m, f);
+ else if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, f);
+ else
+ mount_enter_dead(m, f);
+ break;
+
+ case MOUNT_REMOUNTING:
+ case MOUNT_REMOUNTING_SIGKILL:
+ case MOUNT_REMOUNTING_SIGTERM:
+
+ m->reload_result = f;
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, MOUNT_SUCCESS);
+ else
+ mount_enter_dead(m, MOUNT_SUCCESS);
+
+ break;
+
+ case MOUNT_UNMOUNTING:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGTERM:
+
+ if (f == MOUNT_SUCCESS)
+ mount_enter_dead(m, f);
+ else if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, f);
+ else
+ mount_enter_dead(m, f);
+ break;
+
+ default:
+ assert_not_reached("Uh, control process died at wrong time.");
+ }
+
+ /* Notify clients about changed exit status */
+ unit_add_to_dbus_queue(u);
+}
+
+static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+ assert(elapsed == 1);
+ assert(w == &m->timer_watch);
+
+ switch (m->state) {
+
+ case MOUNT_MOUNTING:
+ case MOUNT_MOUNTING_DONE:
+ log_warning("%s mounting timed out. Stopping.", u->id);
+ mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
+ break;
+
+ case MOUNT_REMOUNTING:
+ log_warning("%s remounting timed out. Stopping.", u->id);
+ m->reload_result = MOUNT_FAILURE_TIMEOUT;
+ mount_enter_mounted(m, MOUNT_SUCCESS);
+ break;
+
+ case MOUNT_UNMOUNTING:
+ log_warning("%s unmounting timed out. Stopping.", u->id);
+ mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
+ break;
+
+ case MOUNT_MOUNTING_SIGTERM:
+ if (m->exec_context.send_sigkill) {
+ log_warning("%s mounting timed out. Killing.", u->id);
+ mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ else
+ mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case MOUNT_REMOUNTING_SIGTERM:
+ if (m->exec_context.send_sigkill) {
+ log_warning("%s remounting timed out. Killing.", u->id);
+ mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ else
+ mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case MOUNT_UNMOUNTING_SIGTERM:
+ if (m->exec_context.send_sigkill) {
+ log_warning("%s unmounting timed out. Killing.", u->id);
+ mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ else
+ mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case MOUNT_MOUNTING_SIGKILL:
+ case MOUNT_REMOUNTING_SIGKILL:
+ case MOUNT_UNMOUNTING_SIGKILL:
+ log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id);
+
+ if (m->from_proc_self_mountinfo)
+ mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
+ else
+ mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
+ break;
+
+ default:
+ assert_not_reached("Timeout at wrong time.");
+ }
+}
+
+static int mount_add_one(
+ Manager *m,
+ const char *what,
+ const char *where,
+ const char *options,
+ const char *fstype,
+ int passno,
+ bool from_proc_self_mountinfo,
+ bool set_flags) {
+ int r;
+ Unit *u;
+ bool delete;
+ char *e, *w = NULL, *o = NULL, *f = NULL;
+ MountParameters *p;
+
+ assert(m);
+ assert(what);
+ assert(where);
+ assert(options);
+ assert(fstype);
+
+ assert(!set_flags || from_proc_self_mountinfo);
+
+ /* Ignore API mount points. They should never be referenced in
+ * dependencies ever. */
+ if (mount_point_is_api(where) || mount_point_ignore(where))
+ return 0;
+
+ if (streq(fstype, "autofs"))
+ return 0;
+
+ /* probably some kind of swap, ignore */
+ if (!is_path(where))
+ return 0;
+
+ e = unit_name_from_path(where, ".mount");
+ if (!e)
+ return -ENOMEM;
+
+ u = manager_get_unit(m, e);
+ if (!u) {
+ delete = true;
+
+ u = unit_new(m, sizeof(Mount));
+ if (!u) {
+ free(e);
+ return -ENOMEM;
+ }
+
+ r = unit_add_name(u, e);
+ free(e);
+
+ if (r < 0)
+ goto fail;
+
+ MOUNT(u)->where = strdup(where);
+ if (!MOUNT(u)->where) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ unit_add_to_load_queue(u);
+ } else {
+ delete = false;
+ free(e);
+ }
+
+ if (!(w = strdup(what)) ||
+ !(o = strdup(options)) ||
+ !(f = strdup(fstype))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (from_proc_self_mountinfo) {
+ p = &MOUNT(u)->parameters_proc_self_mountinfo;
+
+ if (set_flags) {
+ MOUNT(u)->is_mounted = true;
+ MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
+ MOUNT(u)->just_changed = !streq_ptr(p->options, o);
+ }
+
+ MOUNT(u)->from_proc_self_mountinfo = true;
+ } else {
+ p = &MOUNT(u)->parameters_etc_fstab;
+ MOUNT(u)->from_etc_fstab = true;
+ }
+
+ free(p->what);
+ p->what = w;
+
+ free(p->options);
+ p->options = o;
+
+ free(p->fstype);
+ p->fstype = f;
+
+ p->passno = passno;
+
+ unit_add_to_dbus_queue(u);
+
+ return 0;
+
+fail:
+ free(w);
+ free(o);
+ free(f);
+
+ if (delete && u)
+ unit_free(u);
+
+ return r;
+}
+
+static int mount_find_pri(char *options) {
+ char *end, *pri;
+ unsigned long r;
+
+ if (!(pri = mount_test_option(options, "pri")))
+ return 0;
+
+ pri += 4;
+
+ errno = 0;
+ r = strtoul(pri, &end, 10);
+
+ if (errno != 0)
+ return -errno;
+
+ if (end == pri || (*end != ',' && *end != 0))
+ return -EINVAL;
+
+ return (int) r;
+}
+
+static int mount_load_etc_fstab(Manager *m) {
+ FILE *f;
+ int r = 0;
+ struct mntent* me;
+
+ assert(m);
+
+ errno = 0;
+ if (!(f = setmntent("/etc/fstab", "r")))
+ return -errno;
+
+ while ((me = getmntent(f))) {
+ char *where, *what;
+ int k;
+
+ if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(where = strdup(me->mnt_dir))) {
+ free(what);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (what[0] == '/')
+ path_kill_slashes(what);
+
+ if (where[0] == '/')
+ path_kill_slashes(where);
+
+ if (streq(me->mnt_type, "swap")) {
+ int pri;
+
+ if ((pri = mount_find_pri(me->mnt_opts)) < 0)
+ k = pri;
+ else
+ k = swap_add_one(m,
+ what,
+ NULL,
+ pri,
+ !!mount_test_option(me->mnt_opts, "noauto"),
+ !!mount_test_option(me->mnt_opts, "nofail"),
+ !!mount_test_option(me->mnt_opts, "comment=systemd.swapon"),
+ false);
+ } else
+ k = mount_add_one(m, what, where, me->mnt_opts, me->mnt_type, me->mnt_passno, false, false);
+
+ free(what);
+ free(where);
+
+ if (k < 0)
+ r = k;
+ }
+
+finish:
+
+ endmntent(f);
+ return r;
+}
+
+static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
+ int r = 0;
+ unsigned i;
+ char *device, *path, *options, *options2, *fstype, *d, *p, *o;
+
+ assert(m);
+
+ rewind(m->proc_self_mountinfo);
+
+ for (i = 1;; i++) {
+ int k;
+
+ device = path = options = options2 = fstype = d = p = o = NULL;
+
+ if ((k = fscanf(m->proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%ms" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%ms " /* (9) file system type */
+ "%ms" /* (10) mount source */
+ "%ms" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path,
+ &options,
+ &fstype,
+ &device,
+ &options2)) != 5) {
+
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+ goto clean_up;
+ }
+
+ if (asprintf(&o, "%s,%s", options, options2) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(d = cunescape(device)) ||
+ !(p = cunescape(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((k = mount_add_one(m, d, p, o, fstype, 0, true, set_flags)) < 0)
+ r = k;
+
+clean_up:
+ free(device);
+ free(path);
+ free(options);
+ free(options2);
+ free(fstype);
+ free(d);
+ free(p);
+ free(o);
+ }
+
+finish:
+ free(device);
+ free(path);
+ free(options);
+ free(options2);
+ free(fstype);
+ free(d);
+ free(p);
+ free(o);
+
+ return r;
+}
+
+static void mount_shutdown(Manager *m) {
+ assert(m);
+
+ if (m->proc_self_mountinfo) {
+ fclose(m->proc_self_mountinfo);
+ m->proc_self_mountinfo = NULL;
+ }
+}
+
+static int mount_enumerate(Manager *m) {
+ int r;
+ struct epoll_event ev;
+ assert(m);
+
+ if (!m->proc_self_mountinfo) {
+ if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ return -errno;
+
+ m->mount_watch.type = WATCH_MOUNT;
+ m->mount_watch.fd = fileno(m->proc_self_mountinfo);
+
+ zero(ev);
+ ev.events = EPOLLPRI;
+ ev.data.ptr = &m->mount_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
+ return -errno;
+ }
+
+ if ((r = mount_load_etc_fstab(m)) < 0)
+ goto fail;
+
+ if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ mount_shutdown(m);
+ return r;
+}
+
+void mount_fd_event(Manager *m, int events) {
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(events & EPOLLPRI);
+
+ /* The manager calls this for every fd event happening on the
+ * /proc/self/mountinfo file, which informs us about mounting
+ * table changes */
+
+ if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) {
+ log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r));
+
+ /* Reset flags, just in case, for later calls */
+ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
+ Mount *mount = MOUNT(u);
+
+ mount->is_mounted = mount->just_mounted = mount->just_changed = false;
+ }
+
+ return;
+ }
+
+ manager_dispatch_load_queue(m);
+
+ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
+ Mount *mount = MOUNT(u);
+
+ if (!mount->is_mounted) {
+ /* This has just been unmounted. */
+
+ mount->from_proc_self_mountinfo = false;
+
+ switch (mount->state) {
+
+ case MOUNT_MOUNTED:
+ mount_enter_dead(mount, MOUNT_SUCCESS);
+ break;
+
+ default:
+ mount_set_state(mount, mount->state);
+ break;
+
+ }
+
+ } else if (mount->just_mounted || mount->just_changed) {
+
+ /* New or changed mount entry */
+
+ switch (mount->state) {
+
+ case MOUNT_DEAD:
+ case MOUNT_FAILED:
+ mount_enter_mounted(mount, MOUNT_SUCCESS);
+ break;
+
+ case MOUNT_MOUNTING:
+ mount_enter_mounting_done(mount);
+ break;
+
+ default:
+ /* Nothing really changed, but let's
+ * issue an notification call
+ * nonetheless, in case somebody is
+ * waiting for this. (e.g. file system
+ * ro/rw remounts.) */
+ mount_set_state(mount, mount->state);
+ break;
+ }
+ }
+
+ /* Reset the flags for later calls */
+ mount->is_mounted = mount->just_mounted = mount->just_changed = false;
+ }
+}
+
+static void mount_reset_failed(Unit *u) {
+ Mount *m = MOUNT(u);
+
+ assert(m);
+
+ if (m->state == MOUNT_FAILED)
+ mount_set_state(m, MOUNT_DEAD);
+
+ m->result = MOUNT_SUCCESS;
+ m->reload_result = MOUNT_SUCCESS;
+}
+
+static int mount_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+ Mount *m = MOUNT(u);
+ int r = 0;
+ Set *pid_set = NULL;
+
+ assert(m);
+
+ if (who == KILL_MAIN) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes");
+ return -ESRCH;
+ }
+
+ if (m->control_pid <= 0 && who == KILL_CONTROL) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (m->control_pid > 0)
+ if (kill(m->control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
+ int q;
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (m->control_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if ((q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, pid_set)) < 0)
+ if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+finish:
+ if (pid_set)
+ set_free(pid_set);
+
+ return r;
+}
+
+static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
+ [MOUNT_DEAD] = "dead",
+ [MOUNT_MOUNTING] = "mounting",
+ [MOUNT_MOUNTING_DONE] = "mounting-done",
+ [MOUNT_MOUNTED] = "mounted",
+ [MOUNT_REMOUNTING] = "remounting",
+ [MOUNT_UNMOUNTING] = "unmounting",
+ [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
+ [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
+ [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
+ [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
+ [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
+ [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
+ [MOUNT_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
+
+static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
+ [MOUNT_EXEC_MOUNT] = "ExecMount",
+ [MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
+ [MOUNT_EXEC_REMOUNT] = "ExecRemount",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
+
+static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
+ [MOUNT_SUCCESS] = "success",
+ [MOUNT_FAILURE_RESOURCES] = "resources",
+ [MOUNT_FAILURE_TIMEOUT] = "timeout",
+ [MOUNT_FAILURE_EXIT_CODE] = "exit-code",
+ [MOUNT_FAILURE_SIGNAL] = "signal",
+ [MOUNT_FAILURE_CORE_DUMP] = "core-dump"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
+
+const UnitVTable mount_vtable = {
+ .suffix = ".mount",
+ .object_size = sizeof(Mount),
+ .sections =
+ "Unit\0"
+ "Mount\0"
+ "Install\0",
+
+ .no_alias = true,
+ .no_instances = true,
+ .show_status = true,
+
+ .init = mount_init,
+ .load = mount_load,
+ .done = mount_done,
+
+ .coldplug = mount_coldplug,
+
+ .dump = mount_dump,
+
+ .start = mount_start,
+ .stop = mount_stop,
+ .reload = mount_reload,
+
+ .kill = mount_kill,
+
+ .serialize = mount_serialize,
+ .deserialize_item = mount_deserialize_item,
+
+ .active_state = mount_active_state,
+ .sub_state_to_string = mount_sub_state_to_string,
+
+ .check_gc = mount_check_gc,
+
+ .sigchld_event = mount_sigchld_event,
+ .timer_event = mount_timer_event,
+
+ .reset_failed = mount_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Mount",
+ .bus_message_handler = bus_mount_message_handler,
+ .bus_invalidating_properties = bus_mount_invalidating_properties,
+
+ .enumerate = mount_enumerate,
+ .shutdown = mount_shutdown
+};
diff --git a/src/core/mount.h b/src/core/mount.h
new file mode 100644
index 0000000..9318444
--- /dev/null
+++ b/src/core/mount.h
@@ -0,0 +1,124 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foomounthfoo
+#define foomounthfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Mount Mount;
+
+#include "unit.h"
+
+typedef enum MountState {
+ MOUNT_DEAD,
+ MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */
+ MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */
+ MOUNT_MOUNTED,
+ MOUNT_REMOUNTING,
+ MOUNT_UNMOUNTING,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_FAILED,
+ _MOUNT_STATE_MAX,
+ _MOUNT_STATE_INVALID = -1
+} MountState;
+
+typedef enum MountExecCommand {
+ MOUNT_EXEC_MOUNT,
+ MOUNT_EXEC_UNMOUNT,
+ MOUNT_EXEC_REMOUNT,
+ _MOUNT_EXEC_COMMAND_MAX,
+ _MOUNT_EXEC_COMMAND_INVALID = -1
+} MountExecCommand;
+
+typedef struct MountParameters {
+ char *what;
+ char *options;
+ char *fstype;
+ int passno;
+} MountParameters;
+
+typedef enum MountResult {
+ MOUNT_SUCCESS,
+ MOUNT_FAILURE_RESOURCES,
+ MOUNT_FAILURE_TIMEOUT,
+ MOUNT_FAILURE_EXIT_CODE,
+ MOUNT_FAILURE_SIGNAL,
+ MOUNT_FAILURE_CORE_DUMP,
+ _MOUNT_RESULT_MAX,
+ _MOUNT_RESULT_INVALID = -1
+} MountResult;
+
+struct Mount {
+ Unit meta;
+
+ char *where;
+
+ MountParameters parameters_etc_fstab;
+ MountParameters parameters_proc_self_mountinfo;
+ MountParameters parameters_fragment;
+
+ bool from_etc_fstab:1;
+ bool from_proc_self_mountinfo:1;
+ bool from_fragment:1;
+
+ /* Used while looking for mount points that vanished or got
+ * added from/to /proc/self/mountinfo */
+ bool is_mounted:1;
+ bool just_mounted:1;
+ bool just_changed:1;
+
+ MountResult result;
+ MountResult reload_result;
+
+ mode_t directory_mode;
+
+ usec_t timeout_usec;
+
+ ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
+ ExecContext exec_context;
+
+ MountState state, deserialized_state;
+
+ ExecCommand* control_command;
+ MountExecCommand control_command_id;
+ pid_t control_pid;
+
+ Watch timer_watch;
+};
+
+extern const UnitVTable mount_vtable;
+
+void mount_fd_event(Manager *m, int events);
+
+const char* mount_state_to_string(MountState i);
+MountState mount_state_from_string(const char *s);
+
+const char* mount_exec_command_to_string(MountExecCommand i);
+MountExecCommand mount_exec_command_from_string(const char *s);
+
+const char* mount_result_to_string(MountResult i);
+MountResult mount_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/namespace.c b/src/core/namespace.c
new file mode 100644
index 0000000..09bc829
--- /dev/null
+++ b/src/core/namespace.c
@@ -0,0 +1,346 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/mount.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <limits.h>
+#include <linux/fs.h>
+
+#include "strv.h"
+#include "util.h"
+#include "namespace.h"
+#include "missing.h"
+
+typedef enum PathMode {
+ /* This is ordered by priority! */
+ INACCESSIBLE,
+ READONLY,
+ PRIVATE,
+ READWRITE
+} PathMode;
+
+typedef struct Path {
+ const char *path;
+ PathMode mode;
+} Path;
+
+static int append_paths(Path **p, char **strv, PathMode mode) {
+ char **i;
+
+ STRV_FOREACH(i, strv) {
+
+ if (!path_is_absolute(*i))
+ return -EINVAL;
+
+ (*p)->path = *i;
+ (*p)->mode = mode;
+ (*p)++;
+ }
+
+ return 0;
+}
+
+static int path_compare(const void *a, const void *b) {
+ const Path *p = a, *q = b;
+
+ if (path_equal(p->path, q->path)) {
+
+ /* If the paths are equal, check the mode */
+ if (p->mode < q->mode)
+ return -1;
+
+ if (p->mode > q->mode)
+ return 1;
+
+ return 0;
+ }
+
+ /* If the paths are not equal, then order prefixes first */
+ if (path_startswith(p->path, q->path))
+ return 1;
+
+ if (path_startswith(q->path, p->path))
+ return -1;
+
+ return 0;
+}
+
+static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
+ Path *f, *t, *previous;
+
+ assert(p);
+ assert(n);
+ assert(need_inaccessible);
+ assert(need_private);
+
+ for (f = p, t = p, previous = NULL; f < p+*n; f++) {
+
+ if (previous && path_equal(f->path, previous->path))
+ continue;
+
+ t->path = f->path;
+ t->mode = f->mode;
+
+ if (t->mode == PRIVATE)
+ *need_private = true;
+
+ if (t->mode == INACCESSIBLE)
+ *need_inaccessible = true;
+
+ previous = t;
+
+ t++;
+ }
+
+ *n = t - p;
+}
+
+static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
+ const char *what;
+ char *where;
+ int r;
+
+ assert(p);
+ assert(root_dir);
+ assert(inaccessible_dir);
+ assert(private_dir);
+
+ if (!(where = strappend(root_dir, p->path)))
+ return -ENOMEM;
+
+ switch (p->mode) {
+
+ case INACCESSIBLE:
+ what = inaccessible_dir;
+ flags |= MS_RDONLY;
+ break;
+
+ case READONLY:
+ flags |= MS_RDONLY;
+ /* Fall through */
+
+ case READWRITE:
+ what = p->path;
+ break;
+
+ case PRIVATE:
+ what = private_dir;
+ break;
+
+ default:
+ assert_not_reached("Unknown mode");
+ }
+
+ if ((r = mount(what, where, NULL, MS_BIND|MS_REC, NULL)) >= 0) {
+ log_debug("Successfully mounted %s to %s", what, where);
+
+ /* The bind mount will always inherit the original
+ * flags. If we want to set any flag we need
+ * to do so in a second independent step. */
+ if (flags)
+ r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
+
+ /* Avoid exponential growth of trees */
+ if (r >= 0 && path_equal(p->path, "/"))
+ r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_UNBINDABLE|flags, NULL);
+
+ if (r < 0) {
+ r = -errno;
+ umount2(where, MNT_DETACH);
+ }
+ }
+
+ free(where);
+ return r;
+}
+
+int setup_namespace(
+ char **writable,
+ char **readable,
+ char **inaccessible,
+ bool private_tmp,
+ unsigned long flags) {
+
+ char
+ tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
+ root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
+ old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
+ inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
+ private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
+
+ Path *paths, *p;
+ unsigned n;
+ bool need_private = false, need_inaccessible = false;
+ bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
+ int r;
+ const char *t;
+
+ n =
+ strv_length(writable) +
+ strv_length(readable) +
+ strv_length(inaccessible) +
+ (private_tmp ? 2 : 1);
+
+ if (!(paths = new(Path, n)))
+ return -ENOMEM;
+
+ p = paths;
+ if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
+ (r = append_paths(&p, readable, READONLY)) < 0 ||
+ (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
+ goto fail;
+
+ if (private_tmp) {
+ p->path = "/tmp";
+ p->mode = PRIVATE;
+ p++;
+ }
+
+ p->path = "/";
+ p->mode = READWRITE;
+ p++;
+
+ assert(paths + n == p);
+
+ qsort(paths, n, sizeof(Path), path_compare);
+ drop_duplicates(paths, &n, &need_inaccessible, &need_private);
+
+ if (!mkdtemp(tmp_dir)) {
+ r = -errno;
+ goto fail;
+ }
+ remove_tmp = true;
+
+ memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
+ if (mkdir(root_dir, 0777) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ remove_root = true;
+
+ if (need_inaccessible) {
+ memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
+ if (mkdir(inaccessible_dir, 0) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ remove_inaccessible = true;
+ }
+
+ if (need_private) {
+ mode_t u;
+
+ memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
+
+ u = umask(0000);
+ if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
+ umask(u);
+
+ r = -errno;
+ goto fail;
+ }
+
+ umask(u);
+ remove_private = true;
+ }
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* Remount / as SLAVE so that nothing mounted in the namespace
+ shows up in the parent */
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ for (p = paths; p < paths + n; p++)
+ if ((r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags)) < 0)
+ goto undo_mounts;
+
+ memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
+ if (!mkdtemp(old_root_dir)) {
+ r = -errno;
+ goto undo_mounts;
+ }
+ remove_old_root = true;
+
+ if (chdir(root_dir) < 0) {
+ r = -errno;
+ goto undo_mounts;
+ }
+
+ if (pivot_root(root_dir, old_root_dir) < 0) {
+ r = -errno;
+ goto undo_mounts;
+ }
+
+ t = old_root_dir + sizeof(root_dir) - 1;
+ if (umount2(t, MNT_DETACH) < 0)
+ /* At this point it's too late to turn anything back,
+ * since we are already in the new root. */
+ return -errno;
+
+ if (rmdir(t) < 0)
+ return -errno;
+
+ return 0;
+
+undo_mounts:
+
+ for (p--; p >= paths; p--) {
+ char full_path[PATH_MAX];
+
+ snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
+ char_array_0(full_path);
+
+ umount2(full_path, MNT_DETACH);
+ }
+
+fail:
+ if (remove_old_root)
+ rmdir(old_root_dir);
+
+ if (remove_inaccessible)
+ rmdir(inaccessible_dir);
+
+ if (remove_private)
+ rmdir(private_dir);
+
+ if (remove_root)
+ rmdir(root_dir);
+
+ if (remove_tmp)
+ rmdir(tmp_dir);
+
+ free(paths);
+
+ return r;
+}
diff --git a/src/core/namespace.h b/src/core/namespace.h
new file mode 100644
index 0000000..7cf1ded
--- /dev/null
+++ b/src/core/namespace.h
@@ -0,0 +1,34 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foonamespacehfoo
+#define foonamespacehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+int setup_namespace(
+ char **writable,
+ char **readable,
+ char **inaccessible,
+ bool private_tmp,
+ unsigned long flags);
+
+#endif
diff --git a/src/core/path.c b/src/core/path.c
new file mode 100644
index 0000000..1d50885
--- /dev/null
+++ b/src/core/path.c
@@ -0,0 +1,771 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/inotify.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "unit.h"
+#include "unit-name.h"
+#include "path.h"
+#include "mkdir.h"
+#include "dbus-path.h"
+#include "special.h"
+#include "bus-errors.h"
+
+static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
+ [PATH_DEAD] = UNIT_INACTIVE,
+ [PATH_WAITING] = UNIT_ACTIVE,
+ [PATH_RUNNING] = UNIT_ACTIVE,
+ [PATH_FAILED] = UNIT_FAILED
+};
+
+int path_spec_watch(PathSpec *s, Unit *u) {
+
+ static const int flags_table[_PATH_TYPE_MAX] = {
+ [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
+ [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
+ [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
+ [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY,
+ [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
+ };
+
+ bool exists = false;
+ char *k, *slash;
+ int r;
+
+ assert(u);
+ assert(s);
+
+ path_spec_unwatch(s, u);
+
+ if (!(k = strdup(s->path)))
+ return -ENOMEM;
+
+ if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
+ exists = true;
+
+ do {
+ int flags;
+
+ /* This assumes the path was passed through path_kill_slashes()! */
+ if (!(slash = strrchr(k, '/')))
+ break;
+
+ /* Trim the path at the last slash. Keep the slash if it's the root dir. */
+ slash[slash == k] = 0;
+
+ flags = IN_MOVE_SELF;
+ if (!exists)
+ flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
+
+ if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
+ exists = true;
+ } while (slash != k);
+
+ return 0;
+
+fail:
+ free(k);
+
+ path_spec_unwatch(s, u);
+ return r;
+}
+
+void path_spec_unwatch(PathSpec *s, Unit *u) {
+
+ if (s->inotify_fd < 0)
+ return;
+
+ unit_unwatch_fd(u, &s->watch);
+
+ close_nointr_nofail(s->inotify_fd);
+ s->inotify_fd = -1;
+}
+
+int path_spec_fd_event(PathSpec *s, uint32_t events) {
+ uint8_t *buf = NULL;
+ struct inotify_event *e;
+ ssize_t k;
+ int l;
+ int r = 0;
+
+ if (events != EPOLLIN) {
+ log_error("Got Invalid poll event on inotify.");
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
+ log_error("FIONREAD failed: %m");
+ r = -errno;
+ goto out;
+ }
+
+ assert(l > 0);
+
+ if (!(buf = malloc(l))) {
+ log_error("Failed to allocate buffer: %m");
+ r = -errno;
+ goto out;
+ }
+
+ if ((k = read(s->inotify_fd, buf, l)) < 0) {
+ log_error("Failed to read inotify event: %m");
+ r = -errno;
+ goto out;
+ }
+
+ e = (struct inotify_event*) buf;
+
+ while (k > 0) {
+ size_t step;
+
+ if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
+ s->primary_wd == e->wd)
+ r = 1;
+
+ step = sizeof(struct inotify_event) + e->len;
+ assert(step <= (size_t) k);
+
+ e = (struct inotify_event*) ((uint8_t*) e + step);
+ k -= step;
+ }
+out:
+ free(buf);
+ return r;
+}
+
+static bool path_spec_check_good(PathSpec *s, bool initial) {
+ bool good = false;
+
+ switch (s->type) {
+
+ case PATH_EXISTS:
+ good = access(s->path, F_OK) >= 0;
+ break;
+
+ case PATH_EXISTS_GLOB:
+ good = glob_exists(s->path) > 0;
+ break;
+
+ case PATH_DIRECTORY_NOT_EMPTY: {
+ int k;
+
+ k = dir_is_empty(s->path);
+ good = !(k == -ENOENT || k > 0);
+ break;
+ }
+
+ case PATH_CHANGED:
+ case PATH_MODIFIED: {
+ bool b;
+
+ b = access(s->path, F_OK) >= 0;
+ good = !initial && b != s->previous_exists;
+ s->previous_exists = b;
+ break;
+ }
+
+ default:
+ ;
+ }
+
+ return good;
+}
+
+static bool path_spec_startswith(PathSpec *s, const char *what) {
+ return path_startswith(s->path, what);
+}
+
+static void path_spec_mkdir(PathSpec *s, mode_t mode) {
+ int r;
+
+ if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
+ return;
+
+ if ((r = mkdir_p(s->path, mode)) < 0)
+ log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
+}
+
+static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
+ fprintf(f,
+ "%s%s: %s\n",
+ prefix,
+ path_type_to_string(s->type),
+ s->path);
+}
+
+void path_spec_done(PathSpec *s) {
+ assert(s);
+ assert(s->inotify_fd == -1);
+
+ free(s->path);
+}
+
+static void path_init(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ p->directory_mode = 0755;
+}
+
+static void path_done(Unit *u) {
+ Path *p = PATH(u);
+ PathSpec *s;
+
+ assert(p);
+
+ unit_ref_unset(&p->unit);
+
+ while ((s = p->specs)) {
+ path_spec_unwatch(s, u);
+ LIST_REMOVE(PathSpec, spec, p->specs, s);
+ path_spec_done(s);
+ free(s);
+ }
+}
+
+int path_add_one_mount_link(Path *p, Mount *m) {
+ PathSpec *s;
+ int r;
+
+ assert(p);
+ assert(m);
+
+ if (UNIT(p)->load_state != UNIT_LOADED ||
+ UNIT(m)->load_state != UNIT_LOADED)
+ return 0;
+
+ LIST_FOREACH(spec, s, p->specs) {
+
+ if (!path_spec_startswith(s, m->where))
+ continue;
+
+ if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int path_add_mount_links(Path *p) {
+ Unit *other;
+ int r;
+
+ assert(p);
+
+ LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT])
+ if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0)
+ return r;
+
+ return 0;
+}
+
+static int path_verify(Path *p) {
+ assert(p);
+
+ if (UNIT(p)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!p->specs) {
+ log_error("%s lacks path setting. Refusing.", UNIT(p)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int path_add_default_dependencies(Path *p) {
+ int r;
+
+ assert(p);
+
+ if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
+ if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ return r;
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+}
+
+static int path_load(Unit *u) {
+ Path *p = PATH(u);
+ int r;
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_LOADED) {
+
+ if (!UNIT_DEREF(p->unit)) {
+ Unit *x;
+
+ r = unit_load_related_unit(u, ".service", &x);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&p->unit, x);
+ }
+
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
+ if (r < 0)
+ return r;
+
+ if ((r = path_add_mount_links(p)) < 0)
+ return r;
+
+ if (UNIT(p)->default_dependencies)
+ if ((r = path_add_default_dependencies(p)) < 0)
+ return r;
+ }
+
+ return path_verify(p);
+}
+
+static void path_dump(Unit *u, FILE *f, const char *prefix) {
+ Path *p = PATH(u);
+ PathSpec *s;
+
+ assert(p);
+ assert(f);
+
+ fprintf(f,
+ "%sPath State: %s\n"
+ "%sResult: %s\n"
+ "%sUnit: %s\n"
+ "%sMakeDirectory: %s\n"
+ "%sDirectoryMode: %04o\n",
+ prefix, path_state_to_string(p->state),
+ prefix, path_result_to_string(p->result),
+ prefix, UNIT_DEREF(p->unit)->id,
+ prefix, yes_no(p->make_directory),
+ prefix, p->directory_mode);
+
+ LIST_FOREACH(spec, s, p->specs)
+ path_spec_dump(s, f, prefix);
+}
+
+static void path_unwatch(Path *p) {
+ PathSpec *s;
+
+ assert(p);
+
+ LIST_FOREACH(spec, s, p->specs)
+ path_spec_unwatch(s, UNIT(p));
+}
+
+static int path_watch(Path *p) {
+ int r;
+ PathSpec *s;
+
+ assert(p);
+
+ LIST_FOREACH(spec, s, p->specs)
+ if ((r = path_spec_watch(s, UNIT(p))) < 0)
+ return r;
+
+ return 0;
+}
+
+static void path_set_state(Path *p, PathState state) {
+ PathState old_state;
+ assert(p);
+
+ old_state = p->state;
+ p->state = state;
+
+ if (state != PATH_WAITING &&
+ (state != PATH_RUNNING || p->inotify_triggered))
+ path_unwatch(p);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(p)->id,
+ path_state_to_string(old_state),
+ path_state_to_string(state));
+
+ unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static void path_enter_waiting(Path *p, bool initial, bool recheck);
+
+static int path_coldplug(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+ assert(p->state == PATH_DEAD);
+
+ if (p->deserialized_state != p->state) {
+
+ if (p->deserialized_state == PATH_WAITING ||
+ p->deserialized_state == PATH_RUNNING)
+ path_enter_waiting(p, true, true);
+ else
+ path_set_state(p, p->deserialized_state);
+ }
+
+ return 0;
+}
+
+static void path_enter_dead(Path *p, PathResult f) {
+ assert(p);
+
+ if (f != PATH_SUCCESS)
+ p->result = f;
+
+ path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
+}
+
+static void path_enter_running(Path *p) {
+ int r;
+ DBusError error;
+
+ assert(p);
+ dbus_error_init(&error);
+
+ /* Don't start job if we are supposed to go down */
+ if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
+ return;
+
+ if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
+ goto fail;
+
+ p->inotify_triggered = false;
+
+ if ((r = path_watch(p)) < 0)
+ goto fail;
+
+ path_set_state(p, PATH_RUNNING);
+ return;
+
+fail:
+ log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r));
+ path_enter_dead(p, PATH_FAILURE_RESOURCES);
+
+ dbus_error_free(&error);
+}
+
+static bool path_check_good(Path *p, bool initial) {
+ PathSpec *s;
+ bool good = false;
+
+ assert(p);
+
+ LIST_FOREACH(spec, s, p->specs) {
+ good = path_spec_check_good(s, initial);
+
+ if (good)
+ break;
+ }
+
+ return good;
+}
+
+static void path_enter_waiting(Path *p, bool initial, bool recheck) {
+ int r;
+
+ if (recheck)
+ if (path_check_good(p, initial)) {
+ log_debug("%s got triggered.", UNIT(p)->id);
+ path_enter_running(p);
+ return;
+ }
+
+ if ((r = path_watch(p)) < 0)
+ goto fail;
+
+ /* Hmm, so now we have created inotify watches, but the file
+ * might have appeared/been removed by now, so we must
+ * recheck */
+
+ if (recheck)
+ if (path_check_good(p, false)) {
+ log_debug("%s got triggered.", UNIT(p)->id);
+ path_enter_running(p);
+ return;
+ }
+
+ path_set_state(p, PATH_WAITING);
+ return;
+
+fail:
+ log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r));
+ path_enter_dead(p, PATH_FAILURE_RESOURCES);
+}
+
+static void path_mkdir(Path *p) {
+ PathSpec *s;
+
+ assert(p);
+
+ if (!p->make_directory)
+ return;
+
+ LIST_FOREACH(spec, s, p->specs)
+ path_spec_mkdir(s, p->directory_mode);
+}
+
+static int path_start(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+ assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
+
+ if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
+ return -ENOENT;
+
+ path_mkdir(p);
+
+ p->result = PATH_SUCCESS;
+ path_enter_waiting(p, true, true);
+
+ return 0;
+}
+
+static int path_stop(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+ assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
+
+ path_enter_dead(p, PATH_SUCCESS);
+ return 0;
+}
+
+static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Path *p = PATH(u);
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", path_state_to_string(p->state));
+ unit_serialize_item(u, f, "result", path_result_to_string(p->result));
+
+ return 0;
+}
+
+static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Path *p = PATH(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ PathState state;
+
+ if ((state = path_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ p->deserialized_state = state;
+
+ } else if (streq(key, "result")) {
+ PathResult f;
+
+ f = path_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != PATH_SUCCESS)
+ p->result = f;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState path_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[PATH(u)->state];
+}
+
+static const char *path_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return path_state_to_string(PATH(u)->state);
+}
+
+static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
+ Path *p = PATH(u);
+ PathSpec *s;
+ int changed;
+
+ assert(p);
+ assert(fd >= 0);
+
+ if (p->state != PATH_WAITING &&
+ p->state != PATH_RUNNING)
+ return;
+
+ /* log_debug("inotify wakeup on %s.", u->id); */
+
+ LIST_FOREACH(spec, s, p->specs)
+ if (path_spec_owns_inotify_fd(s, fd))
+ break;
+
+ if (!s) {
+ log_error("Got event on unknown fd.");
+ goto fail;
+ }
+
+ changed = path_spec_fd_event(s, events);
+ if (changed < 0)
+ goto fail;
+
+ /* If we are already running, then remember that one event was
+ * dispatched so that we restart the service only if something
+ * actually changed on disk */
+ p->inotify_triggered = true;
+
+ if (changed)
+ path_enter_running(p);
+ else
+ path_enter_waiting(p, false, true);
+
+ return;
+
+fail:
+ path_enter_dead(p, PATH_FAILURE_RESOURCES);
+}
+
+void path_unit_notify(Unit *u, UnitActiveState new_state) {
+ Iterator i;
+ Unit *k;
+
+ if (u->type == UNIT_PATH)
+ return;
+
+ SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
+ Path *p;
+
+ if (k->type != UNIT_PATH)
+ continue;
+
+ if (k->load_state != UNIT_LOADED)
+ continue;
+
+ p = PATH(k);
+
+ if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
+ log_debug("%s got notified about unit deactivation.", UNIT(p)->id);
+
+ /* Hmm, so inotify was triggered since the
+ * last activation, so I guess we need to
+ * recheck what is going on. */
+ path_enter_waiting(p, false, p->inotify_triggered);
+ }
+ }
+}
+
+static void path_reset_failed(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+
+ if (p->state == PATH_FAILED)
+ path_set_state(p, PATH_DEAD);
+
+ p->result = PATH_SUCCESS;
+}
+
+static const char* const path_state_table[_PATH_STATE_MAX] = {
+ [PATH_DEAD] = "dead",
+ [PATH_WAITING] = "waiting",
+ [PATH_RUNNING] = "running",
+ [PATH_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
+
+static const char* const path_type_table[_PATH_TYPE_MAX] = {
+ [PATH_EXISTS] = "PathExists",
+ [PATH_EXISTS_GLOB] = "PathExistsGlob",
+ [PATH_CHANGED] = "PathChanged",
+ [PATH_MODIFIED] = "PathModified",
+ [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
+
+static const char* const path_result_table[_PATH_RESULT_MAX] = {
+ [PATH_SUCCESS] = "success",
+ [PATH_FAILURE_RESOURCES] = "resources"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
+
+const UnitVTable path_vtable = {
+ .suffix = ".path",
+ .object_size = sizeof(Path),
+ .sections =
+ "Unit\0"
+ "Path\0"
+ "Install\0",
+
+ .init = path_init,
+ .done = path_done,
+ .load = path_load,
+
+ .coldplug = path_coldplug,
+
+ .dump = path_dump,
+
+ .start = path_start,
+ .stop = path_stop,
+
+ .serialize = path_serialize,
+ .deserialize_item = path_deserialize_item,
+
+ .active_state = path_active_state,
+ .sub_state_to_string = path_sub_state_to_string,
+
+ .fd_event = path_fd_event,
+
+ .reset_failed = path_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Path",
+ .bus_message_handler = bus_path_message_handler,
+ .bus_invalidating_properties = bus_path_invalidating_properties
+};
diff --git a/src/core/path.h b/src/core/path.h
new file mode 100644
index 0000000..efb6b5e
--- /dev/null
+++ b/src/core/path.h
@@ -0,0 +1,113 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foopathhfoo
+#define foopathhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Path Path;
+
+#include "unit.h"
+#include "mount.h"
+
+typedef enum PathState {
+ PATH_DEAD,
+ PATH_WAITING,
+ PATH_RUNNING,
+ PATH_FAILED,
+ _PATH_STATE_MAX,
+ _PATH_STATE_INVALID = -1
+} PathState;
+
+typedef enum PathType {
+ PATH_EXISTS,
+ PATH_EXISTS_GLOB,
+ PATH_DIRECTORY_NOT_EMPTY,
+ PATH_CHANGED,
+ PATH_MODIFIED,
+ _PATH_TYPE_MAX,
+ _PATH_TYPE_INVALID = -1
+} PathType;
+
+typedef struct PathSpec {
+ char *path;
+
+ Watch watch;
+
+ LIST_FIELDS(struct PathSpec, spec);
+
+ PathType type;
+ int inotify_fd;
+ int primary_wd;
+
+ bool previous_exists;
+} PathSpec;
+
+int path_spec_watch(PathSpec *s, Unit *u);
+void path_spec_unwatch(PathSpec *s, Unit *u);
+int path_spec_fd_event(PathSpec *s, uint32_t events);
+void path_spec_done(PathSpec *s);
+
+static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
+ return s->inotify_fd == fd;
+}
+
+typedef enum PathResult {
+ PATH_SUCCESS,
+ PATH_FAILURE_RESOURCES,
+ _PATH_RESULT_MAX,
+ _PATH_RESULT_INVALID = -1
+} PathResult;
+
+struct Path {
+ Unit meta;
+
+ LIST_HEAD(PathSpec, specs);
+
+ UnitRef unit;
+
+ PathState state, deserialized_state;
+
+ bool inotify_triggered;
+
+ bool make_directory;
+ mode_t directory_mode;
+
+ PathResult result;
+};
+
+void path_unit_notify(Unit *u, UnitActiveState new_state);
+
+/* Called from the mount code figure out if a mount is a dependency of
+ * any of the paths of this path object */
+int path_add_one_mount_link(Path *p, Mount *m);
+
+extern const UnitVTable path_vtable;
+
+const char* path_state_to_string(PathState i);
+PathState path_state_from_string(const char *s);
+
+const char* path_type_to_string(PathType i);
+PathType path_type_from_string(const char *s);
+
+const char* path_result_to_string(PathResult i);
+PathResult path_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/polkit.h b/src/core/polkit.h
new file mode 100644
index 0000000..0d08194
--- /dev/null
+++ b/src/core/polkit.h
@@ -0,0 +1,36 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foopolkithfoo
+#define foopolkithfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <dbus/dbus.h>
+
+int verify_polkit(
+ DBusConnection *c,
+ DBusMessage *request,
+ const char *action,
+ bool interactive,
+ bool *challenge,
+ DBusError *error);
+
+#endif
diff --git a/src/core/securebits.h b/src/core/securebits.h
new file mode 100644
index 0000000..ba0bba5
--- /dev/null
+++ b/src/core/securebits.h
@@ -0,0 +1,45 @@
+#ifndef _LINUX_SECUREBITS_H
+#define _LINUX_SECUREBITS_H 1
+
+/* This is minimal version of Linux' linux/securebits.h header file,
+ * which is licensed GPL2 */
+
+#define SECUREBITS_DEFAULT 0x00000000
+
+/* When set UID 0 has no special privileges. When unset, we support
+ inheritance of root-permissions and suid-root executable under
+ compatibility mode. We raise the effective and inheritable bitmasks
+ *of the executable file* if the effective uid of the new process is
+ 0. If the real uid is 0, we raise the effective (legacy) bit of the
+ executable file. */
+#define SECURE_NOROOT 0
+#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
+
+/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
+ When unset, to provide compatibility with old programs relying on
+ set*uid to gain/lose privilege, transitions to/from uid 0 cause
+ capabilities to be gained/lost. */
+#define SECURE_NO_SETUID_FIXUP 2
+#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
+
+/* When set, a process can retain its capabilities even after
+ transitioning to a non-root user (the set-uid fixup suppressed by
+ bit 2). Bit-4 is cleared when a process calls exec(); setting both
+ bit 4 and 5 will create a barrier through exec that no exec()'d
+ child can use this feature again. */
+#define SECURE_KEEP_CAPS 4
+#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
+
+/* Each securesetting is implemented using two bits. One bit specifies
+ whether the setting is on or off. The other bit specify whether the
+ setting is locked or not. A setting which is locked cannot be
+ changed from user-level. */
+#define issecure_mask(X) (1 << (X))
+#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
+
+#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
+ issecure_mask(SECURE_NO_SETUID_FIXUP) | \
+ issecure_mask(SECURE_KEEP_CAPS))
+#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
+
+#endif /* !_LINUX_SECUREBITS_H */
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
new file mode 100644
index 0000000..a7e1fa4
--- /dev/null
+++ b/src/core/selinux-setup.c
@@ -0,0 +1,112 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include "selinux-setup.h"
+#include "mount-setup.h"
+#include "macro.h"
+#include "util.h"
+#include "log.h"
+#include "label.h"
+
+int selinux_setup(bool *loaded_policy) {
+
+#ifdef HAVE_SELINUX
+ int enforce = 0;
+ usec_t before_load, after_load;
+ security_context_t con;
+ int r;
+
+ assert(loaded_policy);
+
+ /* Make sure getcon() works, which needs /proc and /sys */
+ mount_setup_early();
+
+ /* Already initialized by somebody else? */
+ r = getcon_raw(&con);
+ if (r == 0) {
+ bool initialized;
+
+ initialized = !streq(con, "kernel");
+ freecon(con);
+
+ if (initialized)
+ return 0;
+ }
+
+ /* Make sure we have no fds open while loading the policy and
+ * transitioning */
+ log_close();
+
+ /* Now load the policy */
+ before_load = now(CLOCK_MONOTONIC);
+ r = selinux_init_load_policy(&enforce);
+
+ if (r == 0) {
+ char timespan[FORMAT_TIMESPAN_MAX];
+ char *label;
+
+ label_retest_selinux();
+
+ /* Transition to the new context */
+ r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
+ if (r < 0 || label == NULL) {
+ log_open();
+ log_error("Failed to compute init label, ignoring.");
+ } else {
+ r = setcon(label);
+
+ log_open();
+ if (r < 0)
+ log_error("Failed to transition into init label '%s', ignoring.", label);
+
+ label_free(label);
+ }
+
+ after_load = now(CLOCK_MONOTONIC);
+
+ log_info("Successfully loaded SELinux policy in %s.",
+ format_timespan(timespan, sizeof(timespan), after_load - before_load));
+
+ *loaded_policy = true;
+
+ } else {
+ log_open();
+
+ if (enforce > 0) {
+ log_error("Failed to load SELinux policy. Freezing.");
+ return -EIO;
+ } else
+ log_debug("Unable to load SELinux policy. Ignoring.");
+ }
+#endif
+
+ return 0;
+}
diff --git a/src/core/selinux-setup.h b/src/core/selinux-setup.h
new file mode 100644
index 0000000..6b8fe00
--- /dev/null
+++ b/src/core/selinux-setup.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooselinuxsetuphfoo
+#define fooselinuxsetuphfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+int selinux_setup(bool *loaded_policy);
+
+#endif
diff --git a/src/core/service.c b/src/core/service.c
new file mode 100644
index 0000000..bf2e0a9
--- /dev/null
+++ b/src/core/service.c
@@ -0,0 +1,3797 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <signal.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+
+#include "manager.h"
+#include "unit.h"
+#include "service.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "dbus-service.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "exit-status.h"
+#include "def.h"
+#include "util.h"
+#include "utf8.h"
+
+#ifdef HAVE_SYSV_COMPAT
+
+#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE)
+
+typedef enum RunlevelType {
+ RUNLEVEL_UP,
+ RUNLEVEL_DOWN,
+ RUNLEVEL_SYSINIT
+} RunlevelType;
+
+static const struct {
+ const char *path;
+ const char *target;
+ const RunlevelType type;
+} rcnd_table[] = {
+ /* Standard SysV runlevels for start-up */
+ { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
+ { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
+ { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
+ { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
+ { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
+
+#ifdef TARGET_SUSE
+ /* SUSE style boot.d */
+ { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
+#endif
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ /* Debian style rcS.d */
+ { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
+#endif
+
+ /* Standard SysV runlevels for shutdown */
+ { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
+ { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
+
+ /* Note that the order here matters, as we read the
+ directories in this order, and we want to make sure that
+ sysv_start_priority is known when we first load the
+ unit. And that value we only know from S links. Hence
+ UP/SYSINIT must be read before DOWN */
+};
+
+#define RUNLEVELS_UP "12345"
+/* #define RUNLEVELS_DOWN "06" */
+#define RUNLEVELS_BOOT "bBsS"
+#endif
+
+static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_START_PRE] = UNIT_ACTIVATING,
+ [SERVICE_START] = UNIT_ACTIVATING,
+ [SERVICE_START_POST] = UNIT_ACTIVATING,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+};
+
+static void service_init(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->restart_usec = DEFAULT_RESTART_USEC;
+
+ s->watchdog_watch.type = WATCH_INVALID;
+
+ s->timer_watch.type = WATCH_INVALID;
+#ifdef HAVE_SYSV_COMPAT
+ s->sysv_start_priority = -1;
+ s->sysv_start_priority_from_rcnd = -1;
+#endif
+ s->socket_fd = -1;
+ s->guess_main_pid = true;
+
+ exec_context_init(&s->exec_context);
+
+ RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
+
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+}
+
+static void service_unwatch_control_pid(Service *s) {
+ assert(s);
+
+ if (s->control_pid <= 0)
+ return;
+
+ unit_unwatch_pid(UNIT(s), s->control_pid);
+ s->control_pid = 0;
+}
+
+static void service_unwatch_main_pid(Service *s) {
+ assert(s);
+
+ if (s->main_pid <= 0)
+ return;
+
+ unit_unwatch_pid(UNIT(s), s->main_pid);
+ s->main_pid = 0;
+}
+
+static void service_unwatch_pid_file(Service *s) {
+ if (!s->pid_file_pathspec)
+ return;
+
+ log_debug("Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ path_spec_unwatch(s->pid_file_pathspec, UNIT(s));
+ path_spec_done(s->pid_file_pathspec);
+ free(s->pid_file_pathspec);
+ s->pid_file_pathspec = NULL;
+}
+
+static int service_set_main_pid(Service *s, pid_t pid) {
+ pid_t ppid;
+
+ assert(s);
+
+ if (pid <= 1)
+ return -EINVAL;
+
+ if (pid == getpid())
+ return -EINVAL;
+
+ s->main_pid = pid;
+ s->main_pid_known = true;
+
+ if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
+ log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
+ UNIT(s)->id, (unsigned long) pid);
+
+ s->main_pid_alien = true;
+ } else
+ s->main_pid_alien = false;
+
+ exec_status_start(&s->main_exec_status, pid);
+
+ return 0;
+}
+
+static void service_close_socket_fd(Service *s) {
+ assert(s);
+
+ if (s->socket_fd < 0)
+ return;
+
+ close_nointr_nofail(s->socket_fd);
+ s->socket_fd = -1;
+}
+
+static void service_connection_unref(Service *s) {
+ assert(s);
+
+ if (!UNIT_DEREF(s->accept_socket))
+ return;
+
+ socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
+ unit_ref_unset(&s->accept_socket);
+}
+
+static void service_stop_watchdog(Service *s) {
+ assert(s);
+
+ unit_unwatch_timer(UNIT(s), &s->watchdog_watch);
+ s->watchdog_timestamp.realtime = 0;
+ s->watchdog_timestamp.monotonic = 0;
+}
+
+static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart);
+
+static void service_handle_watchdog(Service *s) {
+ usec_t offset;
+ int r;
+
+ assert(s);
+
+ if (s->watchdog_usec == 0)
+ return;
+
+ offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
+ if (offset >= s->watchdog_usec) {
+ log_error("%s watchdog timeout!", UNIT(s)->id);
+ service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true);
+ return;
+ }
+
+ r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch);
+ if (r < 0)
+ log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r));
+}
+
+static void service_reset_watchdog(Service *s) {
+ assert(s);
+
+ dual_timestamp_get(&s->watchdog_timestamp);
+ service_handle_watchdog(s);
+}
+
+static void service_done(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ free(s->pid_file);
+ s->pid_file = NULL;
+
+#ifdef HAVE_SYSV_COMPAT
+ free(s->sysv_path);
+ s->sysv_path = NULL;
+
+ free(s->sysv_runlevels);
+ s->sysv_runlevels = NULL;
+#endif
+
+ free(s->status_text);
+ s->status_text = NULL;
+
+ exec_context_done(&s->exec_context);
+ exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
+ s->control_command = NULL;
+ s->main_command = NULL;
+
+ /* This will leak a process, but at least no memory or any of
+ * our resources */
+ service_unwatch_main_pid(s);
+ service_unwatch_control_pid(s);
+ service_unwatch_pid_file(s);
+
+ if (s->bus_name) {
+ unit_unwatch_bus_name(u, s->bus_name);
+ free(s->bus_name);
+ s->bus_name = NULL;
+ }
+
+ service_close_socket_fd(s);
+ service_connection_unref(s);
+
+ unit_ref_unset(&s->accept_socket);
+
+ service_stop_watchdog(s);
+
+ unit_unwatch_timer(u, &s->timer_watch);
+}
+
+#ifdef HAVE_SYSV_COMPAT
+static char *sysv_translate_name(const char *name) {
+ char *r;
+
+ if (!(r = new(char, strlen(name) + sizeof(".service"))))
+ return NULL;
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ if (endswith(name, ".sh"))
+ /* Drop Debian-style .sh suffix */
+ strcpy(stpcpy(r, name) - 3, ".service");
+#endif
+#ifdef TARGET_SUSE
+ if (startswith(name, "boot."))
+ /* Drop SuSE-style boot. prefix */
+ strcpy(stpcpy(r, name + 5), ".service");
+#endif
+#ifdef TARGET_FRUGALWARE
+ if (startswith(name, "rc."))
+ /* Drop Frugalware-style rc. prefix */
+ strcpy(stpcpy(r, name + 3), ".service");
+#endif
+ else
+ /* Normal init scripts */
+ strcpy(stpcpy(r, name), ".service");
+
+ return r;
+}
+
+static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
+
+ /* We silently ignore the $ prefix here. According to the LSB
+ * spec it simply indicates whether something is a
+ * standardized name or a distribution-specific one. Since we
+ * just follow what already exists and do not introduce new
+ * uses or names we don't care who introduced a new name. */
+
+ static const char * const table[] = {
+ /* LSB defined facilities */
+ "local_fs", SPECIAL_LOCAL_FS_TARGET,
+#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+#else
+ /* Due to unfortunate name selection in Mandriva,
+ * $network is provided by network-up which is ordered
+ * after network which actually starts interfaces.
+ * To break the loop, just ignore it */
+ "network", SPECIAL_NETWORK_TARGET,
+#endif
+ "named", SPECIAL_NSS_LOOKUP_TARGET,
+ "portmap", SPECIAL_RPCBIND_TARGET,
+ "remote_fs", SPECIAL_REMOTE_FS_TARGET,
+ "syslog", SPECIAL_SYSLOG_TARGET,
+ "time", SPECIAL_TIME_SYNC_TARGET,
+
+ /* common extensions */
+ "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+ "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
+ "null", NULL,
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+#endif
+
+#ifdef TARGET_FEDORA
+ "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+ "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+ "httpd", SPECIAL_HTTP_DAEMON_TARGET,
+#endif
+
+#ifdef TARGET_SUSE
+ "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+#endif
+ };
+
+ unsigned i;
+ char *r;
+ const char *n;
+
+ assert(name);
+ assert(_r);
+
+ n = *name == '$' ? name + 1 : name;
+
+ for (i = 0; i < ELEMENTSOF(table); i += 2) {
+
+ if (!streq(table[i], n))
+ continue;
+
+ if (!table[i+1])
+ return 0;
+
+ if (!(r = strdup(table[i+1])))
+ return -ENOMEM;
+
+ goto finish;
+ }
+
+ /* If we don't know this name, fallback heuristics to figure
+ * out whether something is a target or a service alias. */
+
+ if (*name == '$') {
+ if (!unit_prefix_is_valid(n))
+ return -EINVAL;
+
+ /* Facilities starting with $ are most likely targets */
+ r = unit_name_build(n, NULL, ".target");
+ } else if (filename && streq(name, filename))
+ /* Names equaling the file name of the services are redundant */
+ return 0;
+ else
+ /* Everything else we assume to be normal service names */
+ r = sysv_translate_name(n);
+
+ if (!r)
+ return -ENOMEM;
+
+finish:
+ *_r = r;
+
+ return 1;
+}
+
+static int sysv_fix_order(Service *s) {
+ Unit *other;
+ int r;
+
+ assert(s);
+
+ if (s->sysv_start_priority < 0)
+ return 0;
+
+ /* For each pair of services where at least one lacks a LSB
+ * header, we use the start priority value to order things. */
+
+ LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) {
+ Service *t;
+ UnitDependency d;
+ bool special_s, special_t;
+
+ t = SERVICE(other);
+
+ if (s == t)
+ continue;
+
+ if (UNIT(t)->load_state != UNIT_LOADED)
+ continue;
+
+ if (t->sysv_start_priority < 0)
+ continue;
+
+ /* If both units have modern headers we don't care
+ * about the priorities */
+ if ((UNIT(s)->fragment_path || s->sysv_has_lsb) &&
+ (UNIT(t)->fragment_path || t->sysv_has_lsb))
+ continue;
+
+ special_s = s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels);
+ special_t = t->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, t->sysv_runlevels);
+
+ if (special_t && !special_s)
+ d = UNIT_AFTER;
+ else if (special_s && !special_t)
+ d = UNIT_BEFORE;
+ else if (t->sysv_start_priority < s->sysv_start_priority)
+ d = UNIT_AFTER;
+ else if (t->sysv_start_priority > s->sysv_start_priority)
+ d = UNIT_BEFORE;
+ else
+ continue;
+
+ /* FIXME: Maybe we should compare the name here lexicographically? */
+
+ if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static ExecCommand *exec_command_new(const char *path, const char *arg1) {
+ ExecCommand *c;
+
+ if (!(c = new0(ExecCommand, 1)))
+ return NULL;
+
+ if (!(c->path = strdup(path))) {
+ free(c);
+ return NULL;
+ }
+
+ if (!(c->argv = strv_new(path, arg1, NULL))) {
+ free(c->path);
+ free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+static int sysv_exec_commands(Service *s) {
+ ExecCommand *c;
+
+ assert(s);
+ assert(s->sysv_path);
+
+ if (!(c = exec_command_new(s->sysv_path, "start")))
+ return -ENOMEM;
+ exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c);
+
+ if (!(c = exec_command_new(s->sysv_path, "stop")))
+ return -ENOMEM;
+ exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c);
+
+ if (!(c = exec_command_new(s->sysv_path, "reload")))
+ return -ENOMEM;
+ exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
+
+ return 0;
+}
+
+static int service_load_sysv_path(Service *s, const char *path) {
+ FILE *f;
+ Unit *u;
+ unsigned line = 0;
+ int r;
+ enum {
+ NORMAL,
+ DESCRIPTION,
+ LSB,
+ LSB_DESCRIPTION
+ } state = NORMAL;
+ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
+ struct stat st;
+
+ assert(s);
+ assert(path);
+
+ u = UNIT(s);
+
+ if (!(f = fopen(path, "re"))) {
+ r = errno == ENOENT ? 0 : -errno;
+ goto finish;
+ }
+
+ zero(st);
+ if (fstat(fileno(f), &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ free(s->sysv_path);
+ if (!(s->sysv_path = strdup(path))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ s->sysv_mtime = timespec_load(&st.st_mtim);
+
+ if (null_or_empty(&st)) {
+ u->load_state = UNIT_MASKED;
+ r = 0;
+ goto finish;
+ }
+
+ while (!feof(f)) {
+ char l[LINE_MAX], *t;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ log_error("Failed to read configuration file '%s': %s", path, strerror(-r));
+ goto finish;
+ }
+
+ line++;
+
+ t = strstrip(l);
+ if (*t != '#')
+ continue;
+
+ if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
+ state = LSB;
+ s->sysv_has_lsb = true;
+ continue;
+ }
+
+ if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
+ state = NORMAL;
+ continue;
+ }
+
+ t++;
+ t += strspn(t, WHITESPACE);
+
+ if (state == NORMAL) {
+
+ /* Try to parse Red Hat style chkconfig headers */
+
+ if (startswith_no_case(t, "chkconfig:")) {
+ int start_priority;
+ char runlevels[16], *k;
+
+ state = NORMAL;
+
+ if (sscanf(t+10, "%15s %i %*i",
+ runlevels,
+ &start_priority) != 2) {
+
+ log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line);
+ continue;
+ }
+
+ /* A start priority gathered from the
+ * symlink farms is preferred over the
+ * data from the LSB header. */
+ if (start_priority < 0 || start_priority > 99)
+ log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line);
+ else
+ s->sysv_start_priority = start_priority;
+
+ char_array_0(runlevels);
+ k = delete_chars(runlevels, WHITESPACE "-");
+
+ if (k[0]) {
+ char *d;
+
+ if (!(d = strdup(k))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(s->sysv_runlevels);
+ s->sysv_runlevels = d;
+ }
+
+ } else if (startswith_no_case(t, "description:")) {
+
+ size_t k = strlen(t);
+ char *d;
+ const char *j;
+
+ if (t[k-1] == '\\') {
+ state = DESCRIPTION;
+ t[k-1] = 0;
+ }
+
+ if ((j = strstrip(t+12)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
+
+ free(chkconfig_description);
+ chkconfig_description = d;
+
+ } else if (startswith_no_case(t, "pidfile:")) {
+
+ char *fn;
+
+ state = NORMAL;
+
+ fn = strstrip(t+8);
+ if (!path_is_absolute(fn)) {
+ log_warning("[%s:%u] PID file not absolute. Ignoring.", path, line);
+ continue;
+ }
+
+ if (!(fn = strdup(fn))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(s->pid_file);
+ s->pid_file = fn;
+ }
+
+ } else if (state == DESCRIPTION) {
+
+ /* Try to parse Red Hat style description
+ * continuation */
+
+ size_t k = strlen(t);
+ char *j;
+
+ if (t[k-1] == '\\')
+ t[k-1] = 0;
+ else
+ state = NORMAL;
+
+ if ((j = strstrip(t)) && *j) {
+ char *d = NULL;
+
+ if (chkconfig_description)
+ d = join(chkconfig_description, " ", j, NULL);
+ else
+ d = strdup(j);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(chkconfig_description);
+ chkconfig_description = d;
+ }
+
+ } else if (state == LSB || state == LSB_DESCRIPTION) {
+
+ if (startswith_no_case(t, "Provides:")) {
+ char *i, *w;
+ size_t z;
+
+ state = LSB;
+
+ FOREACH_WORD_QUOTED(w, z, t+9, i) {
+ char *n, *m;
+
+ if (!(n = strndup(w, z))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
+ free(n);
+
+ if (r < 0)
+ goto finish;
+
+ if (r == 0)
+ continue;
+
+ if (unit_name_to_type(m) == UNIT_SERVICE)
+ r = unit_add_name(u, m);
+ else
+ /* NB: SysV targets
+ * which are provided
+ * by a service are
+ * pulled in by the
+ * services, as an
+ * indication that the
+ * generic service is
+ * now available. This
+ * is strictly
+ * one-way. The
+ * targets do NOT pull
+ * in the SysV
+ * services! */
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true);
+
+ if (r < 0)
+ log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r));
+
+ free(m);
+ }
+
+ } else if (startswith_no_case(t, "Required-Start:") ||
+ startswith_no_case(t, "Should-Start:") ||
+ startswith_no_case(t, "X-Start-Before:") ||
+ startswith_no_case(t, "X-Start-After:")) {
+ char *i, *w;
+ size_t z;
+
+ state = LSB;
+
+ FOREACH_WORD_QUOTED(w, z, strchr(t, ':')+1, i) {
+ char *n, *m;
+
+ if (!(n = strndup(w, z))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
+
+ if (r < 0) {
+ log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r));
+ free(n);
+ continue;
+ }
+
+ free(n);
+
+ if (r == 0)
+ continue;
+
+ r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
+
+ if (r < 0)
+ log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", path, line, m, strerror(-r));
+
+ free(m);
+ }
+ } else if (startswith_no_case(t, "Default-Start:")) {
+ char *k, *d;
+
+ state = LSB;
+
+ k = delete_chars(t+14, WHITESPACE "-");
+
+ if (k[0] != 0) {
+ if (!(d = strdup(k))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(s->sysv_runlevels);
+ s->sysv_runlevels = d;
+ }
+
+ } else if (startswith_no_case(t, "Description:")) {
+ char *d, *j;
+
+ state = LSB_DESCRIPTION;
+
+ if ((j = strstrip(t+12)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
+
+ free(long_description);
+ long_description = d;
+
+ } else if (startswith_no_case(t, "Short-Description:")) {
+ char *d, *j;
+
+ state = LSB;
+
+ if ((j = strstrip(t+18)) && *j) {
+ if (!(d = strdup(j))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ } else
+ d = NULL;
+
+ free(short_description);
+ short_description = d;
+
+ } else if (state == LSB_DESCRIPTION) {
+
+ if (startswith(l, "#\t") || startswith(l, "# ")) {
+ char *j;
+
+ if ((j = strstrip(t)) && *j) {
+ char *d = NULL;
+
+ if (long_description)
+ d = join(long_description, " ", t, NULL);
+ else
+ d = strdup(j);
+
+ if (!d) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(long_description);
+ long_description = d;
+ }
+
+ } else
+ state = LSB;
+ }
+ }
+ }
+
+ if ((r = sysv_exec_commands(s)) < 0)
+ goto finish;
+ if (s->sysv_runlevels &&
+ chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) &&
+ chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
+ /* Service has both boot and "up" runlevels
+ configured. Kill the "up" ones. */
+ delete_chars(s->sysv_runlevels, RUNLEVELS_UP);
+ }
+
+ if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
+ /* If there a runlevels configured for this service
+ * but none of the standard ones, then we assume this
+ * is some special kind of service (which might be
+ * needed for early boot) and don't create any links
+ * to it. */
+
+ UNIT(s)->default_dependencies = false;
+
+ /* Don't timeout special services during boot (like fsck) */
+ s->timeout_usec = 0;
+ } else
+ s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+
+ /* Special setting for all SysV services */
+ s->type = SERVICE_FORKING;
+ s->remain_after_exit = !s->pid_file;
+ s->guess_main_pid = false;
+ s->restart = SERVICE_RESTART_NO;
+ s->exec_context.ignore_sigpipe = false;
+
+ if (UNIT(s)->manager->sysv_console)
+ s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+
+ s->exec_context.kill_mode = KILL_PROCESS;
+
+ /* We use the long description only if
+ * no short description is set. */
+
+ if (short_description)
+ description = short_description;
+ else if (chkconfig_description)
+ description = chkconfig_description;
+ else if (long_description)
+ description = long_description;
+ else
+ description = NULL;
+
+ if (description) {
+ char *d;
+
+ if (!(d = strappend(s->sysv_has_lsb ? "LSB: " : "SYSV: ", description))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ u->description = d;
+ }
+
+ /* The priority that has been set in /etc/rcN.d/ hierarchies
+ * takes precedence over what is stored as default in the LSB
+ * header */
+ if (s->sysv_start_priority_from_rcnd >= 0)
+ s->sysv_start_priority = s->sysv_start_priority_from_rcnd;
+
+ u->load_state = UNIT_LOADED;
+ r = 0;
+
+finish:
+
+ if (f)
+ fclose(f);
+
+ free(short_description);
+ free(long_description);
+ free(chkconfig_description);
+
+ return r;
+}
+
+static int service_load_sysv_name(Service *s, const char *name) {
+ char **p;
+
+ assert(s);
+ assert(name);
+
+ /* For SysV services we strip the boot.*, rc.* and *.sh
+ * prefixes/suffixes. */
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ if (endswith(name, ".sh.service"))
+ return -ENOENT;
+#endif
+
+#ifdef TARGET_SUSE
+ if (startswith(name, "boot."))
+ return -ENOENT;
+#endif
+
+#ifdef TARGET_FRUGALWARE
+ if (startswith(name, "rc."))
+ return -ENOENT;
+#endif
+
+ STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
+ char *path;
+ int r;
+
+ path = join(*p, "/", name, NULL);
+ if (!path)
+ return -ENOMEM;
+
+ assert(endswith(path, ".service"));
+ path[strlen(path)-8] = 0;
+
+ r = service_load_sysv_path(s, path);
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
+ if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
+ /* Try Debian style *.sh source'able init scripts */
+ strcat(path, ".sh");
+ r = service_load_sysv_path(s, path);
+ }
+#endif
+ free(path);
+
+#ifdef TARGET_SUSE
+ if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
+ /* Try SUSE style boot.* init scripts */
+
+ path = join(*p, "/boot.", name, NULL);
+ if (!path)
+ return -ENOMEM;
+
+ /* Drop .service suffix */
+ path[strlen(path)-8] = 0;
+ r = service_load_sysv_path(s, path);
+ free(path);
+ }
+#endif
+
+#ifdef TARGET_FRUGALWARE
+ if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
+ /* Try Frugalware style rc.* init scripts */
+
+ path = join(*p, "/rc.", name, NULL);
+ if (!path)
+ return -ENOMEM;
+
+ /* Drop .service suffix */
+ path[strlen(path)-8] = 0;
+ r = service_load_sysv_path(s, path);
+ free(path);
+ }
+#endif
+
+ if (r < 0)
+ return r;
+
+ if ((UNIT(s)->load_state != UNIT_STUB))
+ break;
+ }
+
+ return 0;
+}
+
+static int service_load_sysv(Service *s) {
+ const char *t;
+ Iterator i;
+ int r;
+
+ assert(s);
+
+ /* Load service data from SysV init scripts, preferably with
+ * LSB headers ... */
+
+ if (strv_isempty(UNIT(s)->manager->lookup_paths.sysvinit_path))
+ return 0;
+
+ if ((t = UNIT(s)->id))
+ if ((r = service_load_sysv_name(s, t)) < 0)
+ return r;
+
+ if (UNIT(s)->load_state == UNIT_STUB)
+ SET_FOREACH(t, UNIT(s)->names, i) {
+ if (t == UNIT(s)->id)
+ continue;
+
+ if ((r = service_load_sysv_name(s, t)) < 0)
+ return r;
+
+ if (UNIT(s)->load_state != UNIT_STUB)
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+static int fsck_fix_order(Service *s) {
+ Unit *other;
+ int r;
+
+ assert(s);
+
+ if (s->fsck_passno <= 0)
+ return 0;
+
+ /* For each pair of services where both have an fsck priority
+ * we order things based on it. */
+
+ LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) {
+ Service *t;
+ UnitDependency d;
+
+ t = SERVICE(other);
+
+ if (s == t)
+ continue;
+
+ if (UNIT(t)->load_state != UNIT_LOADED)
+ continue;
+
+ if (t->fsck_passno <= 0)
+ continue;
+
+ if (t->fsck_passno < s->fsck_passno)
+ d = UNIT_AFTER;
+ else if (t->fsck_passno > s->fsck_passno)
+ d = UNIT_BEFORE;
+ else
+ continue;
+
+ if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int service_verify(Service *s) {
+ assert(s);
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!s->exec_command[SERVICE_EXEC_START]) {
+ log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->type != SERVICE_ONESHOT &&
+ s->exec_command[SERVICE_EXEC_START]->command_next) {
+ log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->type == SERVICE_ONESHOT &&
+ s->exec_command[SERVICE_EXEC_RELOAD]) {
+ log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->type == SERVICE_DBUS && !s->bus_name) {
+ log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
+ log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int service_add_default_dependencies(Service *s) {
+ int r;
+
+ assert(s);
+
+ /* Add a number of automatic dependencies useful for the
+ * majority of services. */
+
+ /* First, pull in base system */
+ if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ return r;
+
+ } else if (UNIT(s)->manager->running_as == MANAGER_USER) {
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ /* Second, activate normal shutdown */
+ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+}
+
+static void service_fix_output(Service *s) {
+ assert(s);
+
+ /* If nothing has been explicitly configured, patch default
+ * output in. If input is socket/tty we avoid this however,
+ * since in that case we want output to default to the same
+ * place as we read input from. */
+
+ if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
+ s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
+ s->exec_context.std_input == EXEC_INPUT_NULL)
+ s->exec_context.std_error = UNIT(s)->manager->default_std_error;
+
+ if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
+ s->exec_context.std_input == EXEC_INPUT_NULL)
+ s->exec_context.std_output = UNIT(s)->manager->default_std_output;
+}
+
+static int service_load(Unit *u) {
+ int r;
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ /* Load a .service file */
+ if ((r = unit_load_fragment(u)) < 0)
+ return r;
+
+#ifdef HAVE_SYSV_COMPAT
+ /* Load a classic init script as a fallback, if we couldn't find anything */
+ if (u->load_state == UNIT_STUB)
+ if ((r = service_load_sysv(s)) < 0)
+ return r;
+#endif
+
+ /* Still nothing found? Then let's give up */
+ if (u->load_state == UNIT_STUB)
+ return -ENOENT;
+
+ /* We were able to load something, then let's add in the
+ * dropin directories. */
+ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
+ return r;
+
+ /* This is a new unit? Then let's add in some extras */
+ if (u->load_state == UNIT_LOADED) {
+ service_fix_output(s);
+
+ if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
+ return r;
+
+ if ((r = unit_add_default_cgroups(u)) < 0)
+ return r;
+
+#ifdef HAVE_SYSV_COMPAT
+ if ((r = sysv_fix_order(s)) < 0)
+ return r;
+#endif
+
+ if ((r = fsck_fix_order(s)) < 0)
+ return r;
+
+ if (s->bus_name)
+ if ((r = unit_watch_bus_name(u, s->bus_name)) < 0)
+ return r;
+
+ if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE)
+ s->notify_access = NOTIFY_MAIN;
+
+ if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
+ s->notify_access = NOTIFY_MAIN;
+
+ if (s->type == SERVICE_DBUS || s->bus_name)
+ if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0)
+ return r;
+
+ if (UNIT(s)->default_dependencies)
+ if ((r = service_add_default_dependencies(s)) < 0)
+ return r;
+ }
+
+ return service_verify(s);
+}
+
+static void service_dump(Unit *u, FILE *f, const char *prefix) {
+
+ ServiceExecCommand c;
+ Service *s = SERVICE(u);
+ const char *prefix2;
+ char *p2;
+
+ assert(s);
+
+ p2 = strappend(prefix, "\t");
+ prefix2 = p2 ? p2 : prefix;
+
+ fprintf(f,
+ "%sService State: %s\n"
+ "%sResult: %s\n"
+ "%sReload Result: %s\n"
+ "%sPermissionsStartOnly: %s\n"
+ "%sRootDirectoryStartOnly: %s\n"
+ "%sRemainAfterExit: %s\n"
+ "%sGuessMainPID: %s\n"
+ "%sType: %s\n"
+ "%sRestart: %s\n"
+ "%sNotifyAccess: %s\n",
+ prefix, service_state_to_string(s->state),
+ prefix, service_result_to_string(s->result),
+ prefix, service_result_to_string(s->reload_result),
+ prefix, yes_no(s->permissions_start_only),
+ prefix, yes_no(s->root_directory_start_only),
+ prefix, yes_no(s->remain_after_exit),
+ prefix, yes_no(s->guess_main_pid),
+ prefix, service_type_to_string(s->type),
+ prefix, service_restart_to_string(s->restart),
+ prefix, notify_access_to_string(s->notify_access));
+
+ if (s->control_pid > 0)
+ fprintf(f,
+ "%sControl PID: %lu\n",
+ prefix, (unsigned long) s->control_pid);
+
+ if (s->main_pid > 0)
+ fprintf(f,
+ "%sMain PID: %lu\n"
+ "%sMain PID Known: %s\n"
+ "%sMain PID Alien: %s\n",
+ prefix, (unsigned long) s->main_pid,
+ prefix, yes_no(s->main_pid_known),
+ prefix, yes_no(s->main_pid_alien));
+
+ if (s->pid_file)
+ fprintf(f,
+ "%sPIDFile: %s\n",
+ prefix, s->pid_file);
+
+ if (s->bus_name)
+ fprintf(f,
+ "%sBusName: %s\n"
+ "%sBus Name Good: %s\n",
+ prefix, s->bus_name,
+ prefix, yes_no(s->bus_name_good));
+
+ exec_context_dump(&s->exec_context, f, prefix);
+
+ for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
+
+ if (!s->exec_command[c])
+ continue;
+
+ fprintf(f, "%s-> %s:\n",
+ prefix, service_exec_command_to_string(c));
+
+ exec_command_dump_list(s->exec_command[c], f, prefix2);
+ }
+
+#ifdef HAVE_SYSV_COMPAT
+ if (s->sysv_path)
+ fprintf(f,
+ "%sSysV Init Script Path: %s\n"
+ "%sSysV Init Script has LSB Header: %s\n"
+ "%sSysVEnabled: %s\n",
+ prefix, s->sysv_path,
+ prefix, yes_no(s->sysv_has_lsb),
+ prefix, yes_no(s->sysv_enabled));
+
+ if (s->sysv_start_priority >= 0)
+ fprintf(f,
+ "%sSysVStartPriority: %i\n",
+ prefix, s->sysv_start_priority);
+
+ if (s->sysv_runlevels)
+ fprintf(f, "%sSysVRunLevels: %s\n",
+ prefix, s->sysv_runlevels);
+#endif
+
+ if (s->fsck_passno > 0)
+ fprintf(f,
+ "%sFsckPassNo: %i\n",
+ prefix, s->fsck_passno);
+
+ if (s->status_text)
+ fprintf(f, "%sStatus Text: %s\n",
+ prefix, s->status_text);
+
+ free(p2);
+}
+
+static int service_load_pid_file(Service *s, bool may_warn) {
+ char *k;
+ int r;
+ pid_t pid;
+
+ assert(s);
+
+ if (!s->pid_file)
+ return -ENOENT;
+
+ if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
+ if (may_warn)
+ log_info("PID file %s not readable (yet?) after %s.",
+ s->pid_file, service_state_to_string(s->state));
+ return r;
+ }
+
+ r = parse_pid(k, &pid);
+ free(k);
+
+ if (r < 0)
+ return r;
+
+ if (kill(pid, 0) < 0 && errno != EPERM) {
+ if (may_warn)
+ log_info("PID %lu read from file %s does not exist.",
+ (unsigned long) pid, s->pid_file);
+ return -ESRCH;
+ }
+
+ if (s->main_pid_known) {
+ if (pid == s->main_pid)
+ return 0;
+
+ log_debug("Main PID changing: %lu -> %lu",
+ (unsigned long) s->main_pid, (unsigned long) pid);
+ service_unwatch_main_pid(s);
+ s->main_pid_known = false;
+ } else
+ log_debug("Main PID loaded: %lu", (unsigned long) pid);
+
+ if ((r = service_set_main_pid(s, pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ return r;
+
+ return 0;
+}
+
+static int service_search_main_pid(Service *s) {
+ pid_t pid;
+ int r;
+
+ assert(s);
+
+ /* If we know it anyway, don't ever fallback to unreliable
+ * heuristics */
+ if (s->main_pid_known)
+ return 0;
+
+ if (!s->guess_main_pid)
+ return 0;
+
+ assert(s->main_pid <= 0);
+
+ if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0)
+ return -ENOENT;
+
+ log_debug("Main PID guessed: %lu", (unsigned long) pid);
+ if ((r = service_set_main_pid(s, pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ return r;
+
+ return 0;
+}
+
+static void service_notify_sockets_dead(Service *s, bool failed_permanent) {
+ Iterator i;
+ Unit *u;
+
+ assert(s);
+
+ /* Notifies all our sockets when we die */
+
+ if (s->socket_fd >= 0)
+ return;
+
+ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i)
+ if (u->type == UNIT_SOCKET)
+ socket_notify_service_dead(SOCKET(u), failed_permanent);
+
+ return;
+}
+
+static void service_set_state(Service *s, ServiceState state) {
+ ServiceState old_state;
+ assert(s);
+
+ old_state = s->state;
+ s->state = state;
+
+ service_unwatch_pid_file(s);
+
+ if (state != SERVICE_START_PRE &&
+ state != SERVICE_START &&
+ state != SERVICE_START_POST &&
+ state != SERVICE_RELOAD &&
+ state != SERVICE_STOP &&
+ state != SERVICE_STOP_SIGTERM &&
+ state != SERVICE_STOP_SIGKILL &&
+ state != SERVICE_STOP_POST &&
+ state != SERVICE_FINAL_SIGTERM &&
+ state != SERVICE_FINAL_SIGKILL &&
+ state != SERVICE_AUTO_RESTART)
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ if (state != SERVICE_START &&
+ state != SERVICE_START_POST &&
+ state != SERVICE_RUNNING &&
+ state != SERVICE_RELOAD &&
+ state != SERVICE_STOP &&
+ state != SERVICE_STOP_SIGTERM &&
+ state != SERVICE_STOP_SIGKILL) {
+ service_unwatch_main_pid(s);
+ s->main_command = NULL;
+ }
+
+ if (state != SERVICE_START_PRE &&
+ state != SERVICE_START &&
+ state != SERVICE_START_POST &&
+ state != SERVICE_RELOAD &&
+ state != SERVICE_STOP &&
+ state != SERVICE_STOP_SIGTERM &&
+ state != SERVICE_STOP_SIGKILL &&
+ state != SERVICE_STOP_POST &&
+ state != SERVICE_FINAL_SIGTERM &&
+ state != SERVICE_FINAL_SIGKILL) {
+ service_unwatch_control_pid(s);
+ s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+ }
+
+ if (state == SERVICE_DEAD ||
+ state == SERVICE_STOP ||
+ state == SERVICE_STOP_SIGTERM ||
+ state == SERVICE_STOP_SIGKILL ||
+ state == SERVICE_STOP_POST ||
+ state == SERVICE_FINAL_SIGTERM ||
+ state == SERVICE_FINAL_SIGKILL ||
+ state == SERVICE_FAILED ||
+ state == SERVICE_AUTO_RESTART)
+ service_notify_sockets_dead(s, false);
+
+ if (state != SERVICE_START_PRE &&
+ state != SERVICE_START &&
+ state != SERVICE_START_POST &&
+ state != SERVICE_RUNNING &&
+ state != SERVICE_RELOAD &&
+ state != SERVICE_STOP &&
+ state != SERVICE_STOP_SIGTERM &&
+ state != SERVICE_STOP_SIGKILL &&
+ state != SERVICE_STOP_POST &&
+ state != SERVICE_FINAL_SIGTERM &&
+ state != SERVICE_FINAL_SIGKILL &&
+ !(state == SERVICE_DEAD && UNIT(s)->job)) {
+ service_close_socket_fd(s);
+ service_connection_unref(s);
+ }
+
+ if (state == SERVICE_STOP)
+ service_stop_watchdog(s);
+
+ /* For the inactive states unit_notify() will trim the cgroup,
+ * but for exit we have to do that ourselves... */
+ if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
+ cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
+
+ if (old_state != state)
+ log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], s->reload_result == SERVICE_SUCCESS);
+ s->reload_result = SERVICE_SUCCESS;
+}
+
+static int service_coldplug(Unit *u) {
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(s);
+ assert(s->state == SERVICE_DEAD);
+
+ if (s->deserialized_state != s->state) {
+
+ if (s->deserialized_state == SERVICE_START_PRE ||
+ s->deserialized_state == SERVICE_START ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL ||
+ s->deserialized_state == SERVICE_STOP_POST ||
+ s->deserialized_state == SERVICE_FINAL_SIGTERM ||
+ s->deserialized_state == SERVICE_FINAL_SIGKILL ||
+ s->deserialized_state == SERVICE_AUTO_RESTART) {
+
+ if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) {
+ usec_t k;
+
+ k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec;
+
+ if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0)
+ return r;
+ }
+ }
+
+ if ((s->deserialized_state == SERVICE_START &&
+ (s->type == SERVICE_FORKING ||
+ s->type == SERVICE_DBUS ||
+ s->type == SERVICE_ONESHOT ||
+ s->type == SERVICE_NOTIFY)) ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RUNNING ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL)
+ if (s->main_pid > 0)
+ if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
+ return r;
+
+ if (s->deserialized_state == SERVICE_START_PRE ||
+ s->deserialized_state == SERVICE_START ||
+ s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RELOAD ||
+ s->deserialized_state == SERVICE_STOP ||
+ s->deserialized_state == SERVICE_STOP_SIGTERM ||
+ s->deserialized_state == SERVICE_STOP_SIGKILL ||
+ s->deserialized_state == SERVICE_STOP_POST ||
+ s->deserialized_state == SERVICE_FINAL_SIGTERM ||
+ s->deserialized_state == SERVICE_FINAL_SIGKILL)
+ if (s->control_pid > 0)
+ if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ return r;
+
+ if (s->deserialized_state == SERVICE_START_POST ||
+ s->deserialized_state == SERVICE_RUNNING)
+ service_handle_watchdog(s);
+
+ service_set_state(s, s->deserialized_state);
+ }
+ return 0;
+}
+
+static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
+ Iterator i;
+ int r;
+ int *rfds = NULL;
+ unsigned rn_fds = 0;
+ Unit *u;
+
+ assert(s);
+ assert(fds);
+ assert(n_fds);
+
+ if (s->socket_fd >= 0)
+ return 0;
+
+ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
+ int *cfds;
+ unsigned cn_fds;
+ Socket *sock;
+
+ if (u->type != UNIT_SOCKET)
+ continue;
+
+ sock = SOCKET(u);
+
+ if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0)
+ goto fail;
+
+ if (!cfds)
+ continue;
+
+ if (!rfds) {
+ rfds = cfds;
+ rn_fds = cn_fds;
+ } else {
+ int *t;
+
+ if (!(t = new(int, rn_fds+cn_fds))) {
+ free(cfds);
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ memcpy(t, rfds, rn_fds * sizeof(int));
+ memcpy(t+rn_fds, cfds, cn_fds * sizeof(int));
+ free(rfds);
+ free(cfds);
+
+ rfds = t;
+ rn_fds = rn_fds+cn_fds;
+ }
+ }
+
+ *fds = rfds;
+ *n_fds = rn_fds;
+
+ return 0;
+
+fail:
+ free(rfds);
+
+ return r;
+}
+
+static int service_spawn(
+ Service *s,
+ ExecCommand *c,
+ bool timeout,
+ bool pass_fds,
+ bool apply_permissions,
+ bool apply_chroot,
+ bool apply_tty_stdin,
+ bool set_notify_socket,
+ pid_t *_pid) {
+
+ pid_t pid;
+ int r;
+ int *fds = NULL, *fdsbuf = NULL;
+ unsigned n_fds = 0, n_env = 0;
+ char **argv = NULL, **final_env = NULL, **our_env = NULL;
+
+ assert(s);
+ assert(c);
+ assert(_pid);
+
+ if (pass_fds ||
+ s->exec_context.std_input == EXEC_INPUT_SOCKET ||
+ s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
+ s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
+
+ if (s->socket_fd >= 0) {
+ fds = &s->socket_fd;
+ n_fds = 1;
+ } else {
+ if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0)
+ goto fail;
+
+ fds = fdsbuf;
+ }
+ }
+
+ if (timeout && s->timeout_usec) {
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+ } else
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(our_env = new0(char*, 4))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (set_notify_socket)
+ if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (s->main_pid > 0)
+ if (asprintf(our_env + n_env++, "MAINPID=%lu", (unsigned long) s->main_pid) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (s->watchdog_usec > 0)
+ if (asprintf(our_env + n_env++, "WATCHDOG_USEC=%llu", (unsigned long long) s->watchdog_usec) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(final_env = strv_env_merge(2,
+ UNIT(s)->manager->environment,
+ our_env,
+ NULL))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = exec_spawn(c,
+ argv,
+ &s->exec_context,
+ fds, n_fds,
+ final_env,
+ apply_permissions,
+ apply_chroot,
+ apply_tty_stdin,
+ UNIT(s)->manager->confirm_spawn,
+ UNIT(s)->cgroup_bondings,
+ UNIT(s)->cgroup_attributes,
+ &pid);
+
+ if (r < 0)
+ goto fail;
+
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ goto fail;
+
+ free(fdsbuf);
+ strv_free(argv);
+ strv_free(our_env);
+ strv_free(final_env);
+
+ *_pid = pid;
+
+ return 0;
+
+fail:
+ free(fdsbuf);
+ strv_free(argv);
+ strv_free(our_env);
+ strv_free(final_env);
+
+ if (timeout)
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ return r;
+}
+
+static int main_pid_good(Service *s) {
+ assert(s);
+
+ /* Returns 0 if the pid is dead, 1 if it is good, -1 if we
+ * don't know */
+
+ /* If we know the pid file, then lets just check if it is
+ * still valid */
+ if (s->main_pid_known) {
+
+ /* If it's an alien child let's check if it is still
+ * alive ... */
+ if (s->main_pid_alien)
+ return kill(s->main_pid, 0) >= 0 || errno != ESRCH;
+
+ /* .. otherwise assume we'll get a SIGCHLD for it,
+ * which we really should wait for to collect exit
+ * status and code */
+ return s->main_pid > 0;
+ }
+
+ /* We don't know the pid */
+ return -EAGAIN;
+}
+
+static int control_pid_good(Service *s) {
+ assert(s);
+
+ return s->control_pid > 0;
+}
+
+static int cgroup_good(Service *s) {
+ int r;
+
+ assert(s);
+
+ if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0)
+ return r;
+
+ return !r;
+}
+
+static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
+ int r;
+ assert(s);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ if (allow_restart &&
+ !s->forbid_restart &&
+ (s->restart == SERVICE_RESTART_ALWAYS ||
+ (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
+ (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
+ (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
+ s->result == SERVICE_FAILURE_CORE_DUMP)))) {
+
+ r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
+ if (r < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_AUTO_RESTART);
+ } else
+ service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
+
+ s->forbid_restart = false;
+
+ return;
+
+fail:
+ log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r));
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
+}
+
+static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
+
+static void service_enter_stop_post(Service *s, ServiceResult f) {
+ int r;
+ assert(s);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ service_unwatch_control_pid(s);
+
+ if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
+ s->control_command_id = SERVICE_EXEC_STOP_POST;
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ true,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+
+ service_set_state(s, SERVICE_STOP_POST);
+ } else
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
+ int r;
+ Set *pid_set = NULL;
+ bool wait_for_exit = false;
+
+ assert(s);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ if (s->exec_context.kill_mode != KILL_NONE) {
+ int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
+
+ if (s->main_pid > 0) {
+ if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
+ log_warning("Failed to kill main process %li: %m", (long) s->main_pid);
+ else
+ wait_for_exit = !s->main_pid_alien;
+ }
+
+ if (s->control_pid > 0) {
+ if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
+ log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
+ else
+ wait_for_exit = true;
+ }
+
+ if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Exclude the main/control pids from being killed via the cgroup */
+ if (s->main_pid > 0)
+ if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0)
+ goto fail;
+
+ if (s->control_pid > 0)
+ if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
+ goto fail;
+
+ if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning("Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+
+ set_free(pid_set);
+ pid_set = NULL;
+ }
+ }
+
+ if (wait_for_exit) {
+ if (s->timeout_usec > 0)
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+
+ service_set_state(s, state);
+ } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
+ service_enter_stop_post(s, SERVICE_SUCCESS);
+ else
+ service_enter_dead(s, SERVICE_SUCCESS, true);
+
+ return;
+
+fail:
+ log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+
+ if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
+ service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
+ else
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
+
+ if (pid_set)
+ set_free(pid_set);
+}
+
+static void service_enter_stop(Service *s, ServiceResult f) {
+ int r;
+
+ assert(s);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ service_unwatch_control_pid(s);
+
+ if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
+ s->control_command_id = SERVICE_EXEC_STOP;
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ false,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_STOP);
+ } else
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_enter_running(Service *s, ServiceResult f) {
+ int main_pid_ok, cgroup_ok;
+ assert(s);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ main_pid_ok = main_pid_good(s);
+ cgroup_ok = cgroup_good(s);
+
+ if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
+ (s->bus_name_good || s->type != SERVICE_DBUS))
+ service_set_state(s, SERVICE_RUNNING);
+ else if (s->remain_after_exit)
+ service_set_state(s, SERVICE_EXITED);
+ else
+ service_enter_stop(s, SERVICE_SUCCESS);
+}
+
+static void service_enter_start_post(Service *s) {
+ int r;
+ assert(s);
+
+ service_unwatch_control_pid(s);
+
+ if (s->watchdog_usec > 0)
+ service_reset_watchdog(s);
+
+ if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
+ s->control_command_id = SERVICE_EXEC_START_POST;
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ false,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_START_POST);
+ } else
+ service_enter_running(s, SERVICE_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_enter_start(Service *s) {
+ pid_t pid;
+ int r;
+ ExecCommand *c;
+
+ assert(s);
+
+ assert(s->exec_command[SERVICE_EXEC_START]);
+ assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT);
+
+ if (s->type == SERVICE_FORKING)
+ service_unwatch_control_pid(s);
+ else
+ service_unwatch_main_pid(s);
+
+ /* We want to ensure that nobody leaks processes from
+ * START_PRE here, so let's go on a killing spree, People
+ * should not spawn long running processes from START_PRE. */
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL);
+
+ if (s->type == SERVICE_FORKING) {
+ s->control_command_id = SERVICE_EXEC_START;
+ c = s->control_command = s->exec_command[SERVICE_EXEC_START];
+
+ s->main_command = NULL;
+ } else {
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+ s->control_command = NULL;
+
+ c = s->main_command = s->exec_command[SERVICE_EXEC_START];
+ }
+
+ if ((r = service_spawn(s,
+ c,
+ s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY,
+ true,
+ true,
+ true,
+ true,
+ s->notify_access != NOTIFY_NONE,
+ &pid)) < 0)
+ goto fail;
+
+ if (s->type == SERVICE_SIMPLE) {
+ /* For simple services we immediately start
+ * the START_POST binaries. */
+
+ service_set_main_pid(s, pid);
+ service_enter_start_post(s);
+
+ } else if (s->type == SERVICE_FORKING) {
+
+ /* For forking services we wait until the start
+ * process exited. */
+
+ s->control_pid = pid;
+ service_set_state(s, SERVICE_START);
+
+ } else if (s->type == SERVICE_ONESHOT ||
+ s->type == SERVICE_DBUS ||
+ s->type == SERVICE_NOTIFY) {
+
+ /* For oneshot services we wait until the start
+ * process exited, too, but it is our main process. */
+
+ /* For D-Bus services we know the main pid right away,
+ * but wait for the bus name to appear on the
+ * bus. Notify services are similar. */
+
+ service_set_main_pid(s, pid);
+ service_set_state(s, SERVICE_START);
+ } else
+ assert_not_reached("Unknown service type");
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_enter_start_pre(Service *s) {
+ int r;
+
+ assert(s);
+
+ service_unwatch_control_pid(s);
+
+ if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
+
+ /* Before we start anything, let's clear up what might
+ * be left from previous runs. */
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL);
+
+ s->control_command_id = SERVICE_EXEC_START_PRE;
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ true,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_START_PRE);
+ } else
+ service_enter_start(s);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
+}
+
+static void service_enter_restart(Service *s) {
+ int r;
+ DBusError error;
+
+ assert(s);
+ dbus_error_init(&error);
+
+ if (UNIT(s)->job) {
+ log_info("Job pending for unit, delaying automatic restart.");
+
+ if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0)
+ goto fail;
+ }
+
+ /* Any units that are bound to this service must also be
+ * restarted. We use JOB_RESTART (instead of the more obvious
+ * JOB_START) here so that those dependency jobs will be added
+ * as well. */
+ r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL);
+ if (r < 0)
+ goto fail;
+
+ log_debug("%s scheduled restart job.", UNIT(s)->id);
+ return;
+
+fail:
+ log_warning("%s failed to schedule restart job: %s", UNIT(s)->id, bus_error(&error, -r));
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
+
+ dbus_error_free(&error);
+}
+
+static void service_enter_reload(Service *s) {
+ int r;
+
+ assert(s);
+
+ service_unwatch_control_pid(s);
+
+ if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
+ s->control_command_id = SERVICE_EXEC_RELOAD;
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ false,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+ service_set_state(s, SERVICE_RELOAD);
+ } else
+ service_enter_running(s, SERVICE_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r));
+ s->reload_result = SERVICE_FAILURE_RESOURCES;
+ service_enter_running(s, SERVICE_SUCCESS);
+}
+
+static void service_run_next_control(Service *s) {
+ int r;
+
+ assert(s);
+ assert(s->control_command);
+ assert(s->control_command->command_next);
+
+ assert(s->control_command_id != SERVICE_EXEC_START);
+
+ s->control_command = s->control_command->command_next;
+ service_unwatch_control_pid(s);
+
+ if ((r = service_spawn(s,
+ s->control_command,
+ true,
+ false,
+ !s->permissions_start_only,
+ !s->root_directory_start_only,
+ s->control_command_id == SERVICE_EXEC_START_PRE ||
+ s->control_command_id == SERVICE_EXEC_STOP_POST,
+ false,
+ &s->control_pid)) < 0)
+ goto fail;
+
+ return;
+
+fail:
+ log_warning("%s failed to run next control task: %s", UNIT(s)->id, strerror(-r));
+
+ if (s->state == SERVICE_START_PRE)
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+ else if (s->state == SERVICE_STOP)
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
+ else if (s->state == SERVICE_STOP_POST)
+ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
+ else if (s->state == SERVICE_RELOAD) {
+ s->reload_result = SERVICE_FAILURE_RESOURCES;
+ service_enter_running(s, SERVICE_SUCCESS);
+ } else
+ service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_run_next_main(Service *s) {
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(s->main_command);
+ assert(s->main_command->command_next);
+ assert(s->type == SERVICE_ONESHOT);
+
+ s->main_command = s->main_command->command_next;
+ service_unwatch_main_pid(s);
+
+ if ((r = service_spawn(s,
+ s->main_command,
+ false,
+ true,
+ true,
+ true,
+ true,
+ s->notify_access != NOTIFY_NONE,
+ &pid)) < 0)
+ goto fail;
+
+ service_set_main_pid(s, pid);
+
+ return;
+
+fail:
+ log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r));
+ service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
+}
+
+static int service_start_limit_test(Service *s) {
+ assert(s);
+
+ if (ratelimit_test(&s->start_limit))
+ return 0;
+
+ switch (s->start_limit_action) {
+
+ case SERVICE_START_LIMIT_NONE:
+ log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id);
+ break;
+
+ case SERVICE_START_LIMIT_REBOOT: {
+ DBusError error;
+ int r;
+
+ dbus_error_init(&error);
+
+ log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id);
+
+ r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
+ if (r < 0) {
+ log_error("Failed to reboot: %s.", bus_error(&error, r));
+ dbus_error_free(&error);
+ }
+
+ break;
+ }
+
+ case SERVICE_START_LIMIT_REBOOT_FORCE:
+ log_warning("%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id);
+ UNIT(s)->manager->exit_code = MANAGER_REBOOT;
+ break;
+
+ case SERVICE_START_LIMIT_REBOOT_IMMEDIATE:
+ log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id);
+ reboot(RB_AUTOBOOT);
+ break;
+
+ default:
+ log_error("start limit action=%i", s->start_limit_action);
+ assert_not_reached("Unknown StartLimitAction.");
+ }
+
+ return -ECANCELED;
+}
+
+static int service_start(Unit *u) {
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(s);
+
+ /* We cannot fulfill this request right now, try again later
+ * please! */
+ if (s->state == SERVICE_STOP ||
+ s->state == SERVICE_STOP_SIGTERM ||
+ s->state == SERVICE_STOP_SIGKILL ||
+ s->state == SERVICE_STOP_POST ||
+ s->state == SERVICE_FINAL_SIGTERM ||
+ s->state == SERVICE_FINAL_SIGKILL)
+ return -EAGAIN;
+
+ /* Already on it! */
+ if (s->state == SERVICE_START_PRE ||
+ s->state == SERVICE_START ||
+ s->state == SERVICE_START_POST)
+ return 0;
+
+ assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART);
+
+ /* Make sure we don't enter a busy loop of some kind. */
+ r = service_start_limit_test(s);
+ if (r < 0) {
+ service_notify_sockets_dead(s, true);
+ return r;
+ }
+
+ s->result = SERVICE_SUCCESS;
+ s->reload_result = SERVICE_SUCCESS;
+ s->main_pid_known = false;
+ s->main_pid_alien = false;
+ s->forbid_restart = false;
+
+ service_enter_start_pre(s);
+ return 0;
+}
+
+static int service_stop(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ /* This is a user request, so don't do restarts on this
+ * shutdown. */
+ s->forbid_restart = true;
+
+ /* Already on it */
+ if (s->state == SERVICE_STOP ||
+ s->state == SERVICE_STOP_SIGTERM ||
+ s->state == SERVICE_STOP_SIGKILL ||
+ s->state == SERVICE_STOP_POST ||
+ s->state == SERVICE_FINAL_SIGTERM ||
+ s->state == SERVICE_FINAL_SIGKILL)
+ return 0;
+
+ /* Don't allow a restart */
+ if (s->state == SERVICE_AUTO_RESTART) {
+ service_set_state(s, SERVICE_DEAD);
+ return 0;
+ }
+
+ /* If there's already something running we go directly into
+ * kill mode. */
+ if (s->state == SERVICE_START_PRE ||
+ s->state == SERVICE_START ||
+ s->state == SERVICE_START_POST ||
+ s->state == SERVICE_RELOAD) {
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
+ return 0;
+ }
+
+ assert(s->state == SERVICE_RUNNING ||
+ s->state == SERVICE_EXITED);
+
+ service_enter_stop(s, SERVICE_SUCCESS);
+ return 0;
+}
+
+static int service_reload(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED);
+
+ service_enter_reload(s);
+ return 0;
+}
+
+static bool service_can_reload(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return !!s->exec_command[SERVICE_EXEC_RELOAD];
+}
+
+static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", service_state_to_string(s->state));
+ unit_serialize_item(u, f, "result", service_result_to_string(s->result));
+ unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
+
+ if (s->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+
+ if (s->main_pid_known && s->main_pid > 0)
+ unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid);
+
+ unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
+
+ if (s->status_text)
+ unit_serialize_item(u, f, "status-text", s->status_text);
+
+ /* FIXME: There's a minor uncleanliness here: if there are
+ * multiple commands attached here, we will start from the
+ * first one again */
+ if (s->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
+
+ if (s->socket_fd >= 0) {
+ int copy;
+
+ if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
+ }
+
+ if (s->main_exec_status.pid > 0) {
+ unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid);
+ dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
+ dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+
+ if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
+ unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
+ unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
+ }
+ }
+ if (dual_timestamp_is_set(&s->watchdog_timestamp))
+ dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
+
+ return 0;
+}
+
+static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ ServiceState state;
+
+ if ((state = service_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ ServiceResult f;
+
+ f = service_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ } else if (streq(key, "reload-result")) {
+ ServiceResult f;
+
+ f = service_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse reload result value %s", value);
+ else if (f != SERVICE_SUCCESS)
+ s->reload_result = f;
+
+ } else if (streq(key, "control-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ s->control_pid = pid;
+ } else if (streq(key, "main-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse main-pid value %s", value);
+ else
+ service_set_main_pid(s, (pid_t) pid);
+ } else if (streq(key, "main-pid-known")) {
+ int b;
+
+ if ((b = parse_boolean(value)) < 0)
+ log_debug("Failed to parse main-pid-known value %s", value);
+ else
+ s->main_pid_known = b;
+ } else if (streq(key, "status-text")) {
+ char *t;
+
+ if ((t = strdup(value))) {
+ free(s->status_text);
+ s->status_text = t;
+ }
+
+ } else if (streq(key, "control-command")) {
+ ServiceExecCommand id;
+
+ if ((id = service_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ s->control_command_id = id;
+ s->control_command = s->exec_command[id];
+ }
+ } else if (streq(key, "socket-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse socket-fd value %s", value);
+ else {
+
+ if (s->socket_fd >= 0)
+ close_nointr_nofail(s->socket_fd);
+ s->socket_fd = fdset_remove(fds, fd);
+ }
+ } else if (streq(key, "main-exec-status-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse main-exec-status-pid value %s", value);
+ else
+ s->main_exec_status.pid = pid;
+ } else if (streq(key, "main-exec-status-code")) {
+ int i;
+
+ if (safe_atoi(value, &i) < 0)
+ log_debug("Failed to parse main-exec-status-code value %s", value);
+ else
+ s->main_exec_status.code = i;
+ } else if (streq(key, "main-exec-status-status")) {
+ int i;
+
+ if (safe_atoi(value, &i) < 0)
+ log_debug("Failed to parse main-exec-status-status value %s", value);
+ else
+ s->main_exec_status.status = i;
+ } else if (streq(key, "main-exec-status-start"))
+ dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
+ else if (streq(key, "main-exec-status-exit"))
+ dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
+ else if (streq(key, "watchdog-timestamp"))
+ dual_timestamp_deserialize(value, &s->watchdog_timestamp);
+ else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState service_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SERVICE(u)->state];
+}
+
+static const char *service_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return service_state_to_string(SERVICE(u)->state);
+}
+
+static bool service_check_gc(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ /* Never clean up services that still have a process around,
+ * even if the service is formally dead. */
+ if (cgroup_good(s) > 0 ||
+ main_pid_good(s) > 0 ||
+ control_pid_good(s) > 0)
+ return true;
+
+#ifdef HAVE_SYSV_COMPAT
+ if (s->sysv_path)
+ return true;
+#endif
+
+ return false;
+}
+
+static bool service_check_snapshot(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return !s->got_socket_fd;
+}
+
+static int service_retry_pid_file(Service *s) {
+ int r;
+
+ assert(s->pid_file);
+ assert(s->state == SERVICE_START || s->state == SERVICE_START_POST);
+
+ r = service_load_pid_file(s, false);
+ if (r < 0)
+ return r;
+
+ service_unwatch_pid_file(s);
+
+ service_enter_running(s, SERVICE_SUCCESS);
+ return 0;
+}
+
+static int service_watch_pid_file(Service *s) {
+ int r;
+
+ log_debug("Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ r = path_spec_watch(s->pid_file_pathspec, UNIT(s));
+ if (r < 0)
+ goto fail;
+
+ /* the pidfile might have appeared just before we set the watch */
+ service_retry_pid_file(s);
+
+ return 0;
+fail:
+ log_error("Failed to set a watch for %s's PID file %s: %s",
+ UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r));
+ service_unwatch_pid_file(s);
+ return r;
+}
+
+static int service_demand_pid_file(Service *s) {
+ PathSpec *ps;
+
+ assert(s->pid_file);
+ assert(!s->pid_file_pathspec);
+
+ ps = new0(PathSpec, 1);
+ if (!ps)
+ return -ENOMEM;
+
+ ps->path = strdup(s->pid_file);
+ if (!ps->path) {
+ free(ps);
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(ps->path);
+
+ /* PATH_CHANGED would not be enough. There are daemons (sendmail) that
+ * keep their PID file open all the time. */
+ ps->type = PATH_MODIFIED;
+ ps->inotify_fd = -1;
+
+ s->pid_file_pathspec = ps;
+
+ return service_watch_pid_file(s);
+}
+
+static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+ assert(fd >= 0);
+ assert(s->state == SERVICE_START || s->state == SERVICE_START_POST);
+ assert(s->pid_file_pathspec);
+ assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd));
+
+ log_debug("inotify event for %s", u->id);
+
+ if (path_spec_fd_event(s->pid_file_pathspec, events) < 0)
+ goto fail;
+
+ if (service_retry_pid_file(s) == 0)
+ return;
+
+ if (service_watch_pid_file(s) < 0)
+ goto fail;
+
+ return;
+fail:
+ service_unwatch_pid_file(s);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
+}
+
+static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ Service *s = SERVICE(u);
+ ServiceResult f;
+
+ assert(s);
+ assert(pid >= 0);
+
+ if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+ f = SERVICE_SUCCESS;
+ else if (code == CLD_EXITED)
+ f = SERVICE_FAILURE_EXIT_CODE;
+ else if (code == CLD_KILLED)
+ f = SERVICE_FAILURE_SIGNAL;
+ else if (code == CLD_DUMPED)
+ f = SERVICE_FAILURE_CORE_DUMP;
+ else
+ assert_not_reached("Unknown code");
+
+ if (s->main_pid == pid) {
+ /* Forking services may occasionally move to a new PID.
+ * As long as they update the PID file before exiting the old
+ * PID, they're fine. */
+ if (service_load_pid_file(s, false) == 0)
+ return;
+
+ s->main_pid = 0;
+ exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
+
+ /* If this is not a forking service than the main
+ * process got started and hence we copy the exit
+ * status so that it is recorded both as main and as
+ * control process exit status */
+ if (s->main_command) {
+ s->main_command->exec_status = s->main_exec_status;
+
+ if (s->main_command->ignore)
+ f = SERVICE_SUCCESS;
+ }
+
+ log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ if (s->main_command &&
+ s->main_command->command_next &&
+ f == SERVICE_SUCCESS) {
+
+ /* There is another command to *
+ * execute, so let's do that. */
+
+ log_debug("%s running next main command for state %s", u->id, service_state_to_string(s->state));
+ service_run_next_main(s);
+
+ } else {
+
+ /* The service exited, so the service is officially
+ * gone. */
+ s->main_command = NULL;
+
+ switch (s->state) {
+
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ case SERVICE_STOP:
+ /* Need to wait until the operation is
+ * done */
+ break;
+
+ case SERVICE_START:
+ if (s->type == SERVICE_ONESHOT) {
+ /* This was our main goal, so let's go on */
+ if (f == SERVICE_SUCCESS)
+ service_enter_start_post(s);
+ else
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ break;
+ } else {
+ assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
+
+ /* Fall through */
+ }
+
+ case SERVICE_RUNNING:
+ service_enter_running(s, f);
+ break;
+
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+
+ if (!control_pid_good(s))
+ service_enter_stop_post(s, f);
+
+ /* If there is still a control process, wait for that first */
+ break;
+
+ default:
+ assert_not_reached("Uh, main process died at wrong time.");
+ }
+ }
+
+ } else if (s->control_pid == pid) {
+
+ s->control_pid = 0;
+
+ if (s->control_command) {
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
+
+ if (s->control_command->ignore)
+ f = SERVICE_SUCCESS;
+ }
+
+ log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+
+ if (f != SERVICE_SUCCESS)
+ s->result = f;
+
+ if (s->control_command &&
+ s->control_command->command_next &&
+ f == SERVICE_SUCCESS) {
+
+ /* There is another command to *
+ * execute, so let's do that. */
+
+ log_debug("%s running next control command for state %s", u->id, service_state_to_string(s->state));
+ service_run_next_control(s);
+
+ } else {
+ /* No further commands for this step, so let's
+ * figure out what to do next */
+
+ s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
+ log_debug("%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state));
+
+ switch (s->state) {
+
+ case SERVICE_START_PRE:
+ if (f == SERVICE_SUCCESS)
+ service_enter_start(s);
+ else
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ break;
+
+ case SERVICE_START:
+ assert(s->type == SERVICE_FORKING);
+
+ if (f != SERVICE_SUCCESS) {
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ break;
+ }
+
+ if (s->pid_file) {
+ bool has_start_post;
+ int r;
+
+ /* Let's try to load the pid file here if we can.
+ * The PID file might actually be created by a START_POST
+ * script. In that case don't worry if the loading fails. */
+
+ has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST];
+ r = service_load_pid_file(s, !has_start_post);
+ if (!has_start_post && r < 0) {
+ r = service_demand_pid_file(s);
+ if (r < 0 || !cgroup_good(s))
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+ break;
+ }
+ } else
+ service_search_main_pid(s);
+
+ service_enter_start_post(s);
+ break;
+
+ case SERVICE_START_POST:
+ if (f != SERVICE_SUCCESS) {
+ service_enter_stop(s, f);
+ break;
+ }
+
+ if (s->pid_file) {
+ int r;
+
+ r = service_load_pid_file(s, true);
+ if (r < 0) {
+ r = service_demand_pid_file(s);
+ if (r < 0 || !cgroup_good(s))
+ service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
+ break;
+ }
+ } else
+ service_search_main_pid(s);
+
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
+ case SERVICE_RELOAD:
+ if (f == SERVICE_SUCCESS) {
+ service_load_pid_file(s, true);
+ service_search_main_pid(s);
+ }
+
+ s->reload_result = f;
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
+ case SERVICE_STOP:
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ break;
+
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+ if (main_pid_good(s) <= 0)
+ service_enter_stop_post(s, f);
+
+ /* If there is still a service
+ * process around, wait until
+ * that one quit, too */
+ break;
+
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ service_enter_dead(s, f, true);
+ break;
+
+ default:
+ assert_not_reached("Uh, control process died at wrong time.");
+ }
+ }
+ }
+
+ /* Notify clients about changed exit status */
+ unit_add_to_dbus_queue(u);
+}
+
+static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+ assert(elapsed == 1);
+
+ if (w == &s->watchdog_watch) {
+ service_handle_watchdog(s);
+ return;
+ }
+
+ assert(w == &s->timer_watch);
+
+ switch (s->state) {
+
+ case SERVICE_START_PRE:
+ case SERVICE_START:
+ log_warning("%s operation timed out. Terminating.", u->id);
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_START_POST:
+ log_warning("%s operation timed out. Stopping.", u->id);
+ service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_RELOAD:
+ log_warning("%s operation timed out. Stopping.", u->id);
+ s->reload_result = SERVICE_FAILURE_TIMEOUT;
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
+ case SERVICE_STOP:
+ log_warning("%s stopping timed out. Terminating.", u->id);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_STOP_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out. Killing.", u->id);
+ service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s stopping timed out. Skipping SIGKILL.", u->id);
+ service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
+ }
+
+ break;
+
+ case SERVICE_STOP_SIGKILL:
+ /* Uh, we sent a SIGKILL and it is still not gone?
+ * Must be something we cannot kill, so let's just be
+ * weirded out and continue */
+
+ log_warning("%s still around after SIGKILL. Ignoring.", u->id);
+ service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_STOP_POST:
+ log_warning("%s stopping timed out (2). Terminating.", u->id);
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_FINAL_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out (2). Killing.", u->id);
+ service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->id);
+ service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
+ }
+
+ break;
+
+ case SERVICE_FINAL_SIGKILL:
+ log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
+ service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true);
+ break;
+
+ case SERVICE_AUTO_RESTART:
+ log_info("%s holdoff time over, scheduling restart.", u->id);
+ service_enter_restart(s);
+ break;
+
+ default:
+ assert_not_reached("Timeout at wrong time.");
+ }
+}
+
+static void service_cgroup_notify_event(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(u);
+
+ log_debug("%s: cgroup is empty", u->id);
+
+ switch (s->state) {
+
+ /* Waiting for SIGCHLD is usually more interesting,
+ * because it includes return codes/signals. Which is
+ * why we ignore the cgroup events for most cases,
+ * except when we don't know pid which to expect the
+ * SIGCHLD for. */
+
+ case SERVICE_START:
+ case SERVICE_START_POST:
+ /* If we were hoping for the daemon to write its PID file,
+ * we can give up now. */
+ if (s->pid_file_pathspec) {
+ log_warning("%s never wrote its PID file. Failing.", UNIT(s)->id);
+ service_unwatch_pid_file(s);
+ if (s->state == SERVICE_START)
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
+ else
+ service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
+ }
+ break;
+
+ case SERVICE_RUNNING:
+ /* service_enter_running() will figure out what to do */
+ service_enter_running(s, SERVICE_SUCCESS);
+ break;
+
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+
+ if (main_pid_good(s) <= 0 && !control_pid_good(s))
+ service_enter_stop_post(s, SERVICE_SUCCESS);
+
+ break;
+
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ if (main_pid_good(s) <= 0 && !control_pid_good(s))
+ service_enter_dead(s, SERVICE_SUCCESS, SERVICE_SUCCESS);
+
+ break;
+
+ default:
+ ;
+ }
+}
+
+static void service_notify_message(Unit *u, pid_t pid, char **tags) {
+ Service *s = SERVICE(u);
+ const char *e;
+
+ assert(u);
+
+ if (s->notify_access == NOTIFY_NONE) {
+ log_warning("%s: Got notification message from PID %lu, but reception is disabled.",
+ u->id, (unsigned long) pid);
+ return;
+ }
+
+ if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
+ log_warning("%s: Got notification message from PID %lu, but reception only permitted for PID %lu",
+ u->id, (unsigned long) pid, (unsigned long) s->main_pid);
+ return;
+ }
+
+ log_debug("%s: Got message", u->id);
+
+ /* Interpret MAINPID= */
+ if ((e = strv_find_prefix(tags, "MAINPID=")) &&
+ (s->state == SERVICE_START ||
+ s->state == SERVICE_START_POST ||
+ s->state == SERVICE_RUNNING ||
+ s->state == SERVICE_RELOAD)) {
+
+ if (parse_pid(e + 8, &pid) < 0)
+ log_warning("Failed to parse notification message %s", e);
+ else {
+ log_debug("%s: got %s", u->id, e);
+ service_set_main_pid(s, pid);
+ }
+ }
+
+ /* Interpret READY= */
+ if (s->type == SERVICE_NOTIFY &&
+ s->state == SERVICE_START &&
+ strv_find(tags, "READY=1")) {
+ log_debug("%s: got READY=1", u->id);
+
+ service_enter_start_post(s);
+ }
+
+ /* Interpret STATUS= */
+ e = strv_find_prefix(tags, "STATUS=");
+ if (e) {
+ char *t;
+
+ if (e[7]) {
+
+ if (!utf8_is_valid(e+7)) {
+ log_warning("Status message in notification is not UTF-8 clean.");
+ return;
+ }
+
+ t = strdup(e+7);
+ if (!t) {
+ log_error("Failed to allocate string.");
+ return;
+ }
+
+ log_debug("%s: got %s", u->id, e);
+
+ free(s->status_text);
+ s->status_text = t;
+ } else {
+ free(s->status_text);
+ s->status_text = NULL;
+ }
+
+ }
+ if (strv_find(tags, "WATCHDOG=1")) {
+ log_debug("%s: got WATCHDOG=1", u->id);
+ service_reset_watchdog(s);
+ }
+
+ /* Notify clients about changed status or main pid */
+ unit_add_to_dbus_queue(u);
+}
+
+#ifdef HAVE_SYSV_COMPAT
+
+#ifdef TARGET_SUSE
+static void sysv_facility_in_insserv_conf(Manager *mgr) {
+ FILE *f=NULL;
+ int r;
+
+ if (!(f = fopen("/etc/insserv.conf", "re"))) {
+ r = errno == ENOENT ? 0 : -errno;
+ goto finish;
+ }
+
+ while (!feof(f)) {
+ char l[LINE_MAX], *t;
+ char **parsed = NULL;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r));
+ goto finish;
+ }
+
+ t = strstrip(l);
+ if (*t != '$' && *t != '<')
+ continue;
+
+ parsed = strv_split(t,WHITESPACE);
+ /* we ignore <interactive>, not used, equivalent to X-Interactive */
+ if (parsed && !startswith_no_case (parsed[0], "<interactive>")) {
+ char *facility;
+ Unit *u;
+ if (sysv_translate_facility(parsed[0], NULL, &facility) < 0)
+ continue;
+ if ((u = manager_get_unit(mgr, facility)) && (u->type == UNIT_TARGET)) {
+ UnitDependency e;
+ char *dep = NULL, *name, **j;
+
+ STRV_FOREACH (j, parsed+1) {
+ if (*j[0]=='+') {
+ e = UNIT_WANTS;
+ name = *j+1;
+ }
+ else {
+ e = UNIT_REQUIRES;
+ name = *j;
+ }
+ if (sysv_translate_facility(name, NULL, &dep) < 0)
+ continue;
+
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true);
+ free(dep);
+ }
+ }
+ free(facility);
+ }
+ strv_free(parsed);
+ }
+finish:
+ if (f)
+ fclose(f);
+
+}
+#endif
+
+static int service_enumerate(Manager *m) {
+ char **p;
+ unsigned i;
+ DIR *d = NULL;
+ char *path = NULL, *fpath = NULL, *name = NULL;
+ Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL;
+ Unit *service;
+ Iterator j;
+ int r;
+
+ assert(m);
+
+ if (m->running_as != MANAGER_SYSTEM)
+ return 0;
+
+ zero(runlevel_services);
+
+ STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
+ for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
+ struct dirent *de;
+
+ free(path);
+ path = join(*p, "/", rcnd_table[i].path, NULL);
+ if (!path) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (d)
+ closedir(d);
+
+ if (!(d = opendir(path))) {
+ if (errno != ENOENT)
+ log_warning("opendir() failed on %s: %s", path, strerror(errno));
+
+ continue;
+ }
+
+ while ((de = readdir(d))) {
+ int a, b;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
+ continue;
+
+ if (strlen(de->d_name) < 4)
+ continue;
+
+ a = undecchar(de->d_name[1]);
+ b = undecchar(de->d_name[2]);
+
+ if (a < 0 || b < 0)
+ continue;
+
+ free(fpath);
+ fpath = join(path, "/", de->d_name, NULL);
+ if (!fpath) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (access(fpath, X_OK) < 0) {
+
+ if (errno != ENOENT)
+ log_warning("access() failed on %s: %s", fpath, strerror(errno));
+
+ continue;
+ }
+
+ free(name);
+ if (!(name = sysv_translate_name(de->d_name + 3))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 0) {
+ log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
+ continue;
+ }
+
+ if (de->d_name[0] == 'S') {
+
+ if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) {
+ SERVICE(service)->sysv_start_priority_from_rcnd =
+ MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd);
+
+ SERVICE(service)->sysv_enabled = true;
+ }
+
+ if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0)
+ goto finish;
+
+ if ((r = set_put(runlevel_services[i], service)) < 0)
+ goto finish;
+
+ } else if (de->d_name[0] == 'K' &&
+ (rcnd_table[i].type == RUNLEVEL_DOWN ||
+ rcnd_table[i].type == RUNLEVEL_SYSINIT)) {
+
+ if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
+ goto finish;
+
+ if ((r = set_put(shutdown_services, service)) < 0)
+ goto finish;
+ }
+ }
+ }
+
+ /* Now we loaded all stubs and are aware of the lowest
+ start-up priority for all services, not let's actually load
+ the services, this will also tell us which services are
+ actually native now */
+ manager_dispatch_load_queue(m);
+
+ /* If this is a native service, rely on native ways to pull in
+ * a service, don't pull it in via sysv rcN.d links. */
+ for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
+ SET_FOREACH(service, runlevel_services[i], j) {
+ service = unit_follow_merge(service);
+
+ if (service->fragment_path)
+ continue;
+
+ if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0)
+ goto finish;
+ }
+
+ /* We honour K links only for halt/reboot. For the normal
+ * runlevels we assume the stop jobs will be implicitly added
+ * by the core logic. Also, we don't really distinguish here
+ * between the runlevels 0 and 6 and just add them to the
+ * special shutdown target. On SUSE the boot.d/ runlevel is
+ * also used for shutdown, so we add links for that too to the
+ * shutdown target.*/
+ SET_FOREACH(service, shutdown_services, j) {
+ service = unit_follow_merge(service);
+
+ if (service->fragment_path)
+ continue;
+
+ if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+#ifdef TARGET_SUSE
+ sysv_facility_in_insserv_conf (m);
+#endif
+
+finish:
+ free(path);
+ free(fpath);
+ free(name);
+
+ for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
+ set_free(runlevel_services[i]);
+ set_free(shutdown_services);
+
+ if (d)
+ closedir(d);
+
+ return r;
+}
+#endif
+
+static void service_bus_name_owner_change(
+ Unit *u,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner) {
+
+ Service *s = SERVICE(u);
+
+ assert(s);
+ assert(name);
+
+ assert(streq(s->bus_name, name));
+ assert(old_owner || new_owner);
+
+ if (old_owner && new_owner)
+ log_debug("%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner);
+ else if (old_owner)
+ log_debug("%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner);
+ else
+ log_debug("%s's D-Bus name %s now registered by %s", u->id, name, new_owner);
+
+ s->bus_name_good = !!new_owner;
+
+ if (s->type == SERVICE_DBUS) {
+
+ /* service_enter_running() will figure out what to
+ * do */
+ if (s->state == SERVICE_RUNNING)
+ service_enter_running(s, SERVICE_SUCCESS);
+ else if (s->state == SERVICE_START && new_owner)
+ service_enter_start_post(s);
+
+ } else if (new_owner &&
+ s->main_pid <= 0 &&
+ (s->state == SERVICE_START ||
+ s->state == SERVICE_START_POST ||
+ s->state == SERVICE_RUNNING ||
+ s->state == SERVICE_RELOAD)) {
+
+ /* Try to acquire PID from bus service */
+ log_debug("Trying to acquire PID from D-Bus name...");
+
+ bus_query_pid(u->manager, name);
+ }
+}
+
+static void service_bus_query_pid_done(
+ Unit *u,
+ const char *name,
+ pid_t pid) {
+
+ Service *s = SERVICE(u);
+
+ assert(s);
+ assert(name);
+
+ log_debug("%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid);
+
+ if (s->main_pid <= 0 &&
+ (s->state == SERVICE_START ||
+ s->state == SERVICE_START_POST ||
+ s->state == SERVICE_RUNNING ||
+ s->state == SERVICE_RELOAD))
+ service_set_main_pid(s, pid);
+}
+
+int service_set_socket_fd(Service *s, int fd, Socket *sock) {
+
+ assert(s);
+ assert(fd >= 0);
+
+ /* This is called by the socket code when instantiating a new
+ * service for a stream socket and the socket needs to be
+ * configured. */
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return -EINVAL;
+
+ if (s->socket_fd >= 0)
+ return -EBUSY;
+
+ if (s->state != SERVICE_DEAD)
+ return -EAGAIN;
+
+ s->socket_fd = fd;
+ s->got_socket_fd = true;
+
+ unit_ref_set(&s->accept_socket, UNIT(sock));
+
+ return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+}
+
+static void service_reset_failed(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ if (s->state == SERVICE_FAILED)
+ service_set_state(s, SERVICE_DEAD);
+
+ s->result = SERVICE_SUCCESS;
+ s->reload_result = SERVICE_SUCCESS;
+}
+
+static bool service_need_daemon_reload(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+#ifdef HAVE_SYSV_COMPAT
+ if (s->sysv_path) {
+ struct stat st;
+
+ zero(st);
+ if (stat(s->sysv_path, &st) < 0)
+ /* What, cannot access this anymore? */
+ return true;
+
+ if (s->sysv_mtime > 0 &&
+ timespec_load(&st.st_mtim) != s->sysv_mtime)
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+ Service *s = SERVICE(u);
+ int r = 0;
+ Set *pid_set = NULL;
+
+ assert(s);
+
+ if (s->main_pid <= 0 && who == KILL_MAIN) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
+ return -ESRCH;
+ }
+
+ if (s->control_pid <= 0 && who == KILL_CONTROL) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (s->control_pid > 0)
+ if (kill(s->control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_MAIN || who == KILL_ALL)
+ if (s->main_pid > 0)
+ if (kill(s->main_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
+ int q;
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ /* Exclude the control/main pid from being killed via the cgroup */
+ if (s->control_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if (s->main_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
+ if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+finish:
+ if (pid_set)
+ set_free(pid_set);
+
+ return r;
+}
+
+static const char* const service_state_table[_SERVICE_STATE_MAX] = {
+ [SERVICE_DEAD] = "dead",
+ [SERVICE_START_PRE] = "start-pre",
+ [SERVICE_START] = "start",
+ [SERVICE_START_POST] = "start-post",
+ [SERVICE_RUNNING] = "running",
+ [SERVICE_EXITED] = "exited",
+ [SERVICE_RELOAD] = "reload",
+ [SERVICE_STOP] = "stop",
+ [SERVICE_STOP_SIGTERM] = "stop-sigterm",
+ [SERVICE_STOP_SIGKILL] = "stop-sigkill",
+ [SERVICE_STOP_POST] = "stop-post",
+ [SERVICE_FINAL_SIGTERM] = "final-sigterm",
+ [SERVICE_FINAL_SIGKILL] = "final-sigkill",
+ [SERVICE_FAILED] = "failed",
+ [SERVICE_AUTO_RESTART] = "auto-restart",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
+
+static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
+ [SERVICE_RESTART_NO] = "no",
+ [SERVICE_RESTART_ON_SUCCESS] = "on-success",
+ [SERVICE_RESTART_ON_FAILURE] = "on-failure",
+ [SERVICE_RESTART_ON_ABORT] = "on-abort",
+ [SERVICE_RESTART_ALWAYS] = "always"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
+
+static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
+ [SERVICE_SIMPLE] = "simple",
+ [SERVICE_FORKING] = "forking",
+ [SERVICE_ONESHOT] = "oneshot",
+ [SERVICE_DBUS] = "dbus",
+ [SERVICE_NOTIFY] = "notify"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
+
+static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
+ [SERVICE_EXEC_START_PRE] = "ExecStartPre",
+ [SERVICE_EXEC_START] = "ExecStart",
+ [SERVICE_EXEC_START_POST] = "ExecStartPost",
+ [SERVICE_EXEC_RELOAD] = "ExecReload",
+ [SERVICE_EXEC_STOP] = "ExecStop",
+ [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
+
+static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
+ [NOTIFY_NONE] = "none",
+ [NOTIFY_MAIN] = "main",
+ [NOTIFY_ALL] = "all"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
+
+static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
+ [SERVICE_SUCCESS] = "success",
+ [SERVICE_FAILURE_RESOURCES] = "resources",
+ [SERVICE_FAILURE_TIMEOUT] = "timeout",
+ [SERVICE_FAILURE_EXIT_CODE] = "exit-code",
+ [SERVICE_FAILURE_SIGNAL] = "signal",
+ [SERVICE_FAILURE_CORE_DUMP] = "core-dump",
+ [SERVICE_FAILURE_WATCHDOG] = "watchdog"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
+
+static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = {
+ [SERVICE_START_LIMIT_NONE] = "none",
+ [SERVICE_START_LIMIT_REBOOT] = "reboot",
+ [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force",
+ [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate"
+};
+DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
+
+const UnitVTable service_vtable = {
+ .suffix = ".service",
+ .object_size = sizeof(Service),
+ .sections =
+ "Unit\0"
+ "Service\0"
+ "Install\0",
+ .show_status = true,
+
+ .init = service_init,
+ .done = service_done,
+ .load = service_load,
+
+ .coldplug = service_coldplug,
+
+ .dump = service_dump,
+
+ .start = service_start,
+ .stop = service_stop,
+ .reload = service_reload,
+
+ .can_reload = service_can_reload,
+
+ .kill = service_kill,
+
+ .serialize = service_serialize,
+ .deserialize_item = service_deserialize_item,
+
+ .active_state = service_active_state,
+ .sub_state_to_string = service_sub_state_to_string,
+
+ .check_gc = service_check_gc,
+ .check_snapshot = service_check_snapshot,
+
+ .sigchld_event = service_sigchld_event,
+ .timer_event = service_timer_event,
+ .fd_event = service_fd_event,
+
+ .reset_failed = service_reset_failed,
+
+ .need_daemon_reload = service_need_daemon_reload,
+
+ .cgroup_notify_empty = service_cgroup_notify_event,
+ .notify_message = service_notify_message,
+
+ .bus_name_owner_change = service_bus_name_owner_change,
+ .bus_query_pid_done = service_bus_query_pid_done,
+
+ .bus_interface = "org.freedesktop.systemd1.Service",
+ .bus_message_handler = bus_service_message_handler,
+ .bus_invalidating_properties = bus_service_invalidating_properties,
+
+#ifdef HAVE_SYSV_COMPAT
+ .enumerate = service_enumerate
+#endif
+};
diff --git a/src/core/service.h b/src/core/service.h
new file mode 100644
index 0000000..60b1051
--- /dev/null
+++ b/src/core/service.h
@@ -0,0 +1,220 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooservicehfoo
+#define fooservicehfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Service Service;
+
+#include "unit.h"
+#include "path.h"
+#include "ratelimit.h"
+#include "service.h"
+
+typedef enum ServiceState {
+ SERVICE_DEAD,
+ SERVICE_START_PRE,
+ SERVICE_START,
+ SERVICE_START_POST,
+ SERVICE_RUNNING,
+ SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
+ SERVICE_RELOAD,
+ SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
+ SERVICE_STOP_SIGTERM,
+ SERVICE_STOP_SIGKILL,
+ SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
+ SERVICE_FINAL_SIGKILL,
+ SERVICE_FAILED,
+ SERVICE_AUTO_RESTART,
+ _SERVICE_STATE_MAX,
+ _SERVICE_STATE_INVALID = -1
+} ServiceState;
+
+typedef enum ServiceRestart {
+ SERVICE_RESTART_NO,
+ SERVICE_RESTART_ON_SUCCESS,
+ SERVICE_RESTART_ON_FAILURE,
+ SERVICE_RESTART_ON_ABORT,
+ SERVICE_RESTART_ALWAYS,
+ _SERVICE_RESTART_MAX,
+ _SERVICE_RESTART_INVALID = -1
+} ServiceRestart;
+
+typedef enum ServiceType {
+ SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */
+ SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
+ SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
+ SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
+ SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
+ _SERVICE_TYPE_MAX,
+ _SERVICE_TYPE_INVALID = -1
+} ServiceType;
+
+typedef enum ServiceExecCommand {
+ SERVICE_EXEC_START_PRE,
+ SERVICE_EXEC_START,
+ SERVICE_EXEC_START_POST,
+ SERVICE_EXEC_RELOAD,
+ SERVICE_EXEC_STOP,
+ SERVICE_EXEC_STOP_POST,
+ _SERVICE_EXEC_COMMAND_MAX,
+ _SERVICE_EXEC_COMMAND_INVALID = -1
+} ServiceExecCommand;
+
+typedef enum NotifyAccess {
+ NOTIFY_NONE,
+ NOTIFY_ALL,
+ NOTIFY_MAIN,
+ _NOTIFY_ACCESS_MAX,
+ _NOTIFY_ACCESS_INVALID = -1
+} NotifyAccess;
+
+typedef enum ServiceResult {
+ SERVICE_SUCCESS,
+ SERVICE_FAILURE_RESOURCES,
+ SERVICE_FAILURE_TIMEOUT,
+ SERVICE_FAILURE_EXIT_CODE,
+ SERVICE_FAILURE_SIGNAL,
+ SERVICE_FAILURE_CORE_DUMP,
+ SERVICE_FAILURE_WATCHDOG,
+ _SERVICE_RESULT_MAX,
+ _SERVICE_RESULT_INVALID = -1
+} ServiceResult;
+
+typedef enum StartLimitAction {
+ SERVICE_START_LIMIT_NONE,
+ SERVICE_START_LIMIT_REBOOT,
+ SERVICE_START_LIMIT_REBOOT_FORCE,
+ SERVICE_START_LIMIT_REBOOT_IMMEDIATE,
+ _SERVICE_START_LIMIT_MAX,
+ _SERVICE_START_LIMIT_INVALID = -1
+} StartLimitAction;
+
+struct Service {
+ Unit meta;
+
+ ServiceType type;
+ ServiceRestart restart;
+
+ /* If set we'll read the main daemon PID from this file */
+ char *pid_file;
+
+ usec_t restart_usec;
+ usec_t timeout_usec;
+
+ dual_timestamp watchdog_timestamp;
+ usec_t watchdog_usec;
+ Watch watchdog_watch;
+
+ ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
+ ExecContext exec_context;
+
+ ServiceState state, deserialized_state;
+
+ /* The exit status of the real main process */
+ ExecStatus main_exec_status;
+
+ /* The currently executed control process */
+ ExecCommand *control_command;
+
+ /* The currently executed main process, which may be NULL if
+ * the main process got started via forking mode and not by
+ * us */
+ ExecCommand *main_command;
+
+ /* The ID of the control command currently being executed */
+ ServiceExecCommand control_command_id;
+
+ pid_t main_pid, control_pid;
+ int socket_fd;
+
+ int fsck_passno;
+
+ bool permissions_start_only;
+ bool root_directory_start_only;
+ bool remain_after_exit;
+ bool guess_main_pid;
+
+ /* If we shut down, remember why */
+ ServiceResult result;
+ ServiceResult reload_result;
+
+ bool main_pid_known:1;
+ bool main_pid_alien:1;
+ bool bus_name_good:1;
+ bool forbid_restart:1;
+ bool got_socket_fd:1;
+#ifdef HAVE_SYSV_COMPAT
+ bool sysv_has_lsb:1;
+ bool sysv_enabled:1;
+ int sysv_start_priority_from_rcnd;
+ int sysv_start_priority;
+
+ char *sysv_path;
+ char *sysv_runlevels;
+ usec_t sysv_mtime;
+#endif
+
+ char *bus_name;
+
+ char *status_text;
+
+ RateLimit start_limit;
+ StartLimitAction start_limit_action;
+
+
+ UnitRef accept_socket;
+
+ Watch timer_watch;
+ PathSpec *pid_file_pathspec;
+
+ NotifyAccess notify_access;
+};
+
+extern const UnitVTable service_vtable;
+
+struct Socket;
+
+int service_set_socket_fd(Service *s, int fd, struct Socket *socket);
+
+const char* service_state_to_string(ServiceState i);
+ServiceState service_state_from_string(const char *s);
+
+const char* service_restart_to_string(ServiceRestart i);
+ServiceRestart service_restart_from_string(const char *s);
+
+const char* service_type_to_string(ServiceType i);
+ServiceType service_type_from_string(const char *s);
+
+const char* service_exec_command_to_string(ServiceExecCommand i);
+ServiceExecCommand service_exec_command_from_string(const char *s);
+
+const char* notify_access_to_string(NotifyAccess i);
+NotifyAccess notify_access_from_string(const char *s);
+
+const char* service_result_to_string(ServiceResult i);
+ServiceResult service_result_from_string(const char *s);
+
+const char* start_limit_action_to_string(StartLimitAction i);
+StartLimitAction start_limit_action_from_string(const char *s);
+
+#endif
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
new file mode 100644
index 0000000..82ec510
--- /dev/null
+++ b/src/core/snapshot.c
@@ -0,0 +1,309 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "unit.h"
+#include "snapshot.h"
+#include "unit-name.h"
+#include "dbus-snapshot.h"
+#include "bus-errors.h"
+
+static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
+ [SNAPSHOT_DEAD] = UNIT_INACTIVE,
+ [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
+};
+
+static void snapshot_init(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(s);
+ assert(UNIT(s)->load_state == UNIT_STUB);
+
+ UNIT(s)->ignore_on_isolate = true;
+ UNIT(s)->ignore_on_snapshot = true;
+}
+
+static void snapshot_set_state(Snapshot *s, SnapshotState state) {
+ SnapshotState old_state;
+ assert(s);
+
+ old_state = s->state;
+ s->state = state;
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(s)->id,
+ snapshot_state_to_string(old_state),
+ snapshot_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int snapshot_load(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ /* Make sure that only snapshots created via snapshot_create()
+ * can be loaded */
+ if (!s->by_snapshot_create && UNIT(s)->manager->n_reloading <= 0)
+ return -ENOENT;
+
+ u->load_state = UNIT_LOADED;
+ return 0;
+}
+
+static int snapshot_coldplug(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(s);
+ assert(s->state == SNAPSHOT_DEAD);
+
+ if (s->deserialized_state != s->state)
+ snapshot_set_state(s, s->deserialized_state);
+
+ return 0;
+}
+
+static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(s);
+ assert(f);
+
+ fprintf(f,
+ "%sSnapshot State: %s\n"
+ "%sClean Up: %s\n",
+ prefix, snapshot_state_to_string(s->state),
+ prefix, yes_no(s->cleanup));
+}
+
+static int snapshot_start(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(s);
+ assert(s->state == SNAPSHOT_DEAD);
+
+ snapshot_set_state(s, SNAPSHOT_ACTIVE);
+
+ if (s->cleanup)
+ unit_add_to_cleanup_queue(u);
+
+ return 0;
+}
+
+static int snapshot_stop(Unit *u) {
+ Snapshot *s = SNAPSHOT(u);
+
+ assert(s);
+ assert(s->state == SNAPSHOT_ACTIVE);
+
+ snapshot_set_state(s, SNAPSHOT_DEAD);
+ return 0;
+}
+
+static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Snapshot *s = SNAPSHOT(u);
+ Unit *other;
+ Iterator i;
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
+ unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
+ SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
+ unit_serialize_item(u, f, "wants", other->id);
+
+ return 0;
+}
+
+static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Snapshot *s = SNAPSHOT(u);
+ int r;
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SnapshotState state;
+
+ if ((state = snapshot_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else if (streq(key, "cleanup")) {
+
+ if ((r = parse_boolean(value)) < 0)
+ log_debug("Failed to parse cleanup value %s", value);
+ else
+ s->cleanup = r;
+
+ } else if (streq(key, "wants")) {
+
+ if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true)) < 0)
+ return r;
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState snapshot_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SNAPSHOT(u)->state];
+}
+
+static const char *snapshot_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return snapshot_state_to_string(SNAPSHOT(u)->state);
+}
+
+int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **_s) {
+ Iterator i;
+ Unit *other, *u = NULL;
+ char *n = NULL;
+ int r;
+ const char *k;
+
+ assert(m);
+ assert(_s);
+
+ if (name) {
+ if (!unit_name_is_valid(name, false)) {
+ dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name);
+ return -EINVAL;
+ }
+
+ if (unit_name_to_type(name) != UNIT_SNAPSHOT) {
+ dbus_set_error(e, BUS_ERROR_UNIT_TYPE_MISMATCH, "Unit name %s lacks snapshot suffix.", name);
+ return -EINVAL;
+ }
+
+ if (manager_get_unit(m, name)) {
+ dbus_set_error(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
+ return -EEXIST;
+ }
+
+ } else {
+
+ for (;;) {
+ if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
+ return -ENOMEM;
+
+ if (!manager_get_unit(m, n))
+ break;
+
+ free(n);
+ }
+
+ name = n;
+ }
+
+ r = manager_load_unit_prepare(m, name, NULL, e, &u);
+ free(n);
+
+ if (r < 0)
+ goto fail;
+
+ SNAPSHOT(u)->by_snapshot_create = true;
+ manager_dispatch_load_queue(m);
+ assert(u->load_state == UNIT_LOADED);
+
+ HASHMAP_FOREACH_KEY(other, k, m->units, i) {
+
+ if (other->ignore_on_snapshot)
+ continue;
+
+ if (k != other->id)
+ continue;
+
+ if (UNIT_VTABLE(other)->check_snapshot)
+ if (!UNIT_VTABLE(other)->check_snapshot(other))
+ continue;
+
+ if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ continue;
+
+ if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true)) < 0)
+ goto fail;
+ }
+
+ SNAPSHOT(u)->cleanup = cleanup;
+ *_s = SNAPSHOT(u);
+
+ return 0;
+
+fail:
+ if (u)
+ unit_add_to_cleanup_queue(u);
+
+ return r;
+}
+
+void snapshot_remove(Snapshot *s) {
+ assert(s);
+
+ unit_add_to_cleanup_queue(UNIT(s));
+}
+
+static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
+ [SNAPSHOT_DEAD] = "dead",
+ [SNAPSHOT_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
+
+const UnitVTable snapshot_vtable = {
+ .suffix = ".snapshot",
+ .object_size = sizeof(Snapshot),
+
+ .no_alias = true,
+ .no_instances = true,
+ .no_gc = true,
+
+ .init = snapshot_init,
+
+ .load = snapshot_load,
+ .coldplug = snapshot_coldplug,
+
+ .dump = snapshot_dump,
+
+ .start = snapshot_start,
+ .stop = snapshot_stop,
+
+ .serialize = snapshot_serialize,
+ .deserialize_item = snapshot_deserialize_item,
+
+ .active_state = snapshot_active_state,
+ .sub_state_to_string = snapshot_sub_state_to_string,
+
+ .bus_interface = "org.freedesktop.systemd1.Snapshot",
+ .bus_message_handler = bus_snapshot_message_handler
+};
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
new file mode 100644
index 0000000..bf92e99
--- /dev/null
+++ b/src/core/snapshot.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosnapshothfoo
+#define foosnapshothfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Snapshot Snapshot;
+
+#include "unit.h"
+
+typedef enum SnapshotState {
+ SNAPSHOT_DEAD,
+ SNAPSHOT_ACTIVE,
+ _SNAPSHOT_STATE_MAX,
+ _SNAPSHOT_STATE_INVALID = -1
+} SnapshotState;
+
+struct Snapshot {
+ Unit meta;
+
+ SnapshotState state, deserialized_state;
+
+ bool cleanup;
+ bool by_snapshot_create:1;
+};
+
+extern const UnitVTable snapshot_vtable;
+
+int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s);
+void snapshot_remove(Snapshot *s);
+
+const char* snapshot_state_to_string(SnapshotState i);
+SnapshotState snapshot_state_from_string(const char *s);
+
+#endif
diff --git a/src/core/socket.c b/src/core/socket.c
new file mode 100644
index 0000000..5b24b34
--- /dev/null
+++ b/src/core/socket.c
@@ -0,0 +1,2218 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <mqueue.h>
+
+#include "unit.h"
+#include "socket.h"
+#include "netinet/tcp.h"
+#include "log.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "strv.h"
+#include "mkdir.h"
+#include "unit-name.h"
+#include "dbus-socket.h"
+#include "missing.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "label.h"
+#include "exit-status.h"
+#include "def.h"
+
+static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
+ [SOCKET_DEAD] = UNIT_INACTIVE,
+ [SOCKET_START_PRE] = UNIT_ACTIVATING,
+ [SOCKET_START_POST] = UNIT_ACTIVATING,
+ [SOCKET_LISTENING] = UNIT_ACTIVE,
+ [SOCKET_RUNNING] = UNIT_ACTIVE,
+ [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
+ [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING,
+ [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING,
+ [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
+ [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SOCKET_FAILED] = UNIT_FAILED
+};
+
+static void socket_init(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ s->backlog = SOMAXCONN;
+ s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->directory_mode = 0755;
+ s->socket_mode = 0666;
+
+ s->max_connections = 64;
+
+ s->priority = -1;
+ s->ip_tos = -1;
+ s->ip_ttl = -1;
+ s->mark = -1;
+
+ exec_context_init(&s->exec_context);
+ s->exec_context.std_output = u->manager->default_std_output;
+ s->exec_context.std_error = u->manager->default_std_error;
+
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+}
+
+static void socket_unwatch_control_pid(Socket *s) {
+ assert(s);
+
+ if (s->control_pid <= 0)
+ return;
+
+ unit_unwatch_pid(UNIT(s), s->control_pid);
+ s->control_pid = 0;
+}
+
+static void socket_done(Unit *u) {
+ Socket *s = SOCKET(u);
+ SocketPort *p;
+
+ assert(s);
+
+ while ((p = s->ports)) {
+ LIST_REMOVE(SocketPort, port, s->ports, p);
+
+ if (p->fd >= 0) {
+ unit_unwatch_fd(UNIT(s), &p->fd_watch);
+ close_nointr_nofail(p->fd);
+ }
+
+ free(p->path);
+ free(p);
+ }
+
+ exec_context_done(&s->exec_context);
+ exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
+ s->control_command = NULL;
+
+ socket_unwatch_control_pid(s);
+
+ unit_ref_unset(&s->service);
+
+ free(s->tcp_congestion);
+ s->tcp_congestion = NULL;
+
+ free(s->bind_to_device);
+ s->bind_to_device = NULL;
+
+ unit_unwatch_timer(u, &s->timer_watch);
+}
+
+static int socket_instantiate_service(Socket *s) {
+ char *prefix, *name;
+ int r;
+ Unit *u;
+
+ assert(s);
+
+ /* This fills in s->service if it isn't filled in yet. For
+ * Accept=yes sockets we create the next connection service
+ * here. For Accept=no this is mostly a NOP since the service
+ * is figured out at load time anyway. */
+
+ if (UNIT_DEREF(s->service))
+ return 0;
+
+ assert(s->accept);
+
+ if (!(prefix = unit_name_to_prefix(UNIT(s)->id)))
+ return -ENOMEM;
+
+ r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted);
+ free(prefix);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u);
+ free(name);
+
+ if (r < 0)
+ return r;
+
+#ifdef HAVE_SYSV_COMPAT
+ if (SERVICE(u)->sysv_path) {
+ log_error("Using SysV services for socket activation is not supported. Refusing.");
+ return -ENOENT;
+ }
+#endif
+
+ u->no_gc = true;
+ unit_ref_set(&s->service, u);
+
+ return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
+}
+
+static bool have_non_accept_socket(Socket *s) {
+ SocketPort *p;
+
+ assert(s);
+
+ if (!s->accept)
+ return true;
+
+ LIST_FOREACH(port, p, s->ports) {
+
+ if (p->type != SOCKET_SOCKET)
+ return true;
+
+ if (!socket_address_can_accept(&p->address))
+ return true;
+ }
+
+ return false;
+}
+
+static int socket_verify(Socket *s) {
+ assert(s);
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!s->ports) {
+ log_error("%s lacks Listen setting. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->accept && have_non_accept_socket(s)) {
+ log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->accept && s->max_connections <= 0) {
+ log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->accept && UNIT_DEREF(s->service)) {
+ log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
+ log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool socket_needs_mount(Socket *s, const char *prefix) {
+ SocketPort *p;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+
+ if (p->type == SOCKET_SOCKET) {
+ if (socket_address_needs_mount(&p->address, prefix))
+ return true;
+ } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) {
+ if (path_startswith(p->path, prefix))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int socket_add_one_mount_link(Socket *s, Mount *m) {
+ int r;
+
+ assert(s);
+ assert(m);
+
+ if (UNIT(s)->load_state != UNIT_LOADED ||
+ UNIT(m)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!socket_needs_mount(s, m->where))
+ return 0;
+
+ if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int socket_add_mount_links(Socket *s) {
+ Unit *other;
+ int r;
+
+ assert(s);
+
+ LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
+ if ((r = socket_add_one_mount_link(s, MOUNT(other))) < 0)
+ return r;
+
+ return 0;
+}
+
+static int socket_add_device_link(Socket *s) {
+ char *t;
+ int r;
+
+ assert(s);
+
+ if (!s->bind_to_device)
+ return 0;
+
+ if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0)
+ return -ENOMEM;
+
+ r = unit_add_node_link(UNIT(s), t, false);
+ free(t);
+
+ return r;
+}
+
+static int socket_add_default_dependencies(Socket *s) {
+ int r;
+ assert(s);
+
+ if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
+ return r;
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+}
+
+static bool socket_has_exec(Socket *s) {
+ unsigned i;
+ assert(s);
+
+ for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++)
+ if (s->exec_command[i])
+ return true;
+
+ return false;
+}
+
+static int socket_load(Unit *u) {
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ return r;
+
+ /* This is a new unit? Then let's add in some extras */
+ if (u->load_state == UNIT_LOADED) {
+
+ if (have_non_accept_socket(s)) {
+
+ if (!UNIT_DEREF(s->service)) {
+ Unit *x;
+
+ r = unit_load_related_unit(u, ".service", &x);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&s->service, x);
+ }
+
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true);
+ if (r < 0)
+ return r;
+ }
+
+ if ((r = socket_add_mount_links(s)) < 0)
+ return r;
+
+ if ((r = socket_add_device_link(s)) < 0)
+ return r;
+
+ if (socket_has_exec(s))
+ if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
+ return r;
+
+ if ((r = unit_add_default_cgroups(u)) < 0)
+ return r;
+
+ if (UNIT(s)->default_dependencies)
+ if ((r = socket_add_default_dependencies(s)) < 0)
+ return r;
+ }
+
+ return socket_verify(s);
+}
+
+static const char* listen_lookup(int family, int type) {
+
+ if (family == AF_NETLINK)
+ return "ListenNetlink";
+
+ if (type == SOCK_STREAM)
+ return "ListenStream";
+ else if (type == SOCK_DGRAM)
+ return "ListenDatagram";
+ else if (type == SOCK_SEQPACKET)
+ return "ListenSequentialPacket";
+
+ assert_not_reached("Unknown socket type");
+ return NULL;
+}
+
+static void socket_dump(Unit *u, FILE *f, const char *prefix) {
+
+ SocketExecCommand c;
+ Socket *s = SOCKET(u);
+ SocketPort *p;
+ const char *prefix2;
+ char *p2;
+
+ assert(s);
+ assert(f);
+
+ p2 = strappend(prefix, "\t");
+ prefix2 = p2 ? p2 : prefix;
+
+ fprintf(f,
+ "%sSocket State: %s\n"
+ "%sResult: %s\n"
+ "%sBindIPv6Only: %s\n"
+ "%sBacklog: %u\n"
+ "%sSocketMode: %04o\n"
+ "%sDirectoryMode: %04o\n"
+ "%sKeepAlive: %s\n"
+ "%sFreeBind: %s\n"
+ "%sTransparent: %s\n"
+ "%sBroadcast: %s\n"
+ "%sPassCredentials: %s\n"
+ "%sPassSecurity: %s\n"
+ "%sTCPCongestion: %s\n",
+ prefix, socket_state_to_string(s->state),
+ prefix, socket_result_to_string(s->result),
+ prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only),
+ prefix, s->backlog,
+ prefix, s->socket_mode,
+ prefix, s->directory_mode,
+ prefix, yes_no(s->keep_alive),
+ prefix, yes_no(s->free_bind),
+ prefix, yes_no(s->transparent),
+ prefix, yes_no(s->broadcast),
+ prefix, yes_no(s->pass_cred),
+ prefix, yes_no(s->pass_sec),
+ prefix, strna(s->tcp_congestion));
+
+ if (s->control_pid > 0)
+ fprintf(f,
+ "%sControl PID: %lu\n",
+ prefix, (unsigned long) s->control_pid);
+
+ if (s->bind_to_device)
+ fprintf(f,
+ "%sBindToDevice: %s\n",
+ prefix, s->bind_to_device);
+
+ if (s->accept)
+ fprintf(f,
+ "%sAccepted: %u\n"
+ "%sNConnections: %u\n"
+ "%sMaxConnections: %u\n",
+ prefix, s->n_accepted,
+ prefix, s->n_connections,
+ prefix, s->max_connections);
+
+ if (s->priority >= 0)
+ fprintf(f,
+ "%sPriority: %i\n",
+ prefix, s->priority);
+
+ if (s->receive_buffer > 0)
+ fprintf(f,
+ "%sReceiveBuffer: %zu\n",
+ prefix, s->receive_buffer);
+
+ if (s->send_buffer > 0)
+ fprintf(f,
+ "%sSendBuffer: %zu\n",
+ prefix, s->send_buffer);
+
+ if (s->ip_tos >= 0)
+ fprintf(f,
+ "%sIPTOS: %i\n",
+ prefix, s->ip_tos);
+
+ if (s->ip_ttl >= 0)
+ fprintf(f,
+ "%sIPTTL: %i\n",
+ prefix, s->ip_ttl);
+
+ if (s->pipe_size > 0)
+ fprintf(f,
+ "%sPipeSize: %zu\n",
+ prefix, s->pipe_size);
+
+ if (s->mark >= 0)
+ fprintf(f,
+ "%sMark: %i\n",
+ prefix, s->mark);
+
+ if (s->mq_maxmsg > 0)
+ fprintf(f,
+ "%sMessageQueueMaxMessages: %li\n",
+ prefix, s->mq_maxmsg);
+
+ if (s->mq_msgsize > 0)
+ fprintf(f,
+ "%sMessageQueueMessageSize: %li\n",
+ prefix, s->mq_msgsize);
+
+ LIST_FOREACH(port, p, s->ports) {
+
+ if (p->type == SOCKET_SOCKET) {
+ const char *t;
+ int r;
+ char *k = NULL;
+
+ if ((r = socket_address_print(&p->address, &k)) < 0)
+ t = strerror(-r);
+ else
+ t = k;
+
+ fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t);
+ free(k);
+ } else if (p->type == SOCKET_SPECIAL)
+ fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
+ else if (p->type == SOCKET_MQUEUE)
+ fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
+ else
+ fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
+ }
+
+ exec_context_dump(&s->exec_context, f, prefix);
+
+ for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
+ if (!s->exec_command[c])
+ continue;
+
+ fprintf(f, "%s-> %s:\n",
+ prefix, socket_exec_command_to_string(c));
+
+ exec_command_dump_list(s->exec_command[c], f, prefix2);
+ }
+
+ free(p2);
+}
+
+static int instance_from_socket(int fd, unsigned nr, char **instance) {
+ socklen_t l;
+ char *r;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ struct sockaddr_storage storage;
+ } local, remote;
+
+ assert(fd >= 0);
+ assert(instance);
+
+ l = sizeof(local);
+ if (getsockname(fd, &local.sa, &l) < 0)
+ return -errno;
+
+ l = sizeof(remote);
+ if (getpeername(fd, &remote.sa, &l) < 0)
+ return -errno;
+
+ switch (local.sa.sa_family) {
+
+ case AF_INET: {
+ uint32_t
+ a = ntohl(local.in.sin_addr.s_addr),
+ b = ntohl(remote.in.sin_addr.s_addr);
+
+ if (asprintf(&r,
+ "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
+ nr,
+ a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
+ ntohs(local.in.sin_port),
+ b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
+ ntohs(remote.in.sin_port)) < 0)
+ return -ENOMEM;
+
+ break;
+ }
+
+ case AF_INET6: {
+ static const char ipv4_prefix[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
+ };
+
+ if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 &&
+ memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
+ const uint8_t
+ *a = local.in6.sin6_addr.s6_addr+12,
+ *b = remote.in6.sin6_addr.s6_addr+12;
+
+ if (asprintf(&r,
+ "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
+ nr,
+ a[0], a[1], a[2], a[3],
+ ntohs(local.in6.sin6_port),
+ b[0], b[1], b[2], b[3],
+ ntohs(remote.in6.sin6_port)) < 0)
+ return -ENOMEM;
+ } else {
+ char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
+
+ if (asprintf(&r,
+ "%u-%s:%u-%s:%u",
+ nr,
+ inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
+ ntohs(local.in6.sin6_port),
+ inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
+ ntohs(remote.in6.sin6_port)) < 0)
+ return -ENOMEM;
+ }
+
+ break;
+ }
+
+ case AF_UNIX: {
+ struct ucred ucred;
+
+ l = sizeof(ucred);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
+ return -errno;
+
+ if (asprintf(&r,
+ "%u-%lu-%lu",
+ nr,
+ (unsigned long) ucred.pid,
+ (unsigned long) ucred.uid) < 0)
+ return -ENOMEM;
+
+ break;
+ }
+
+ default:
+ assert_not_reached("Unhandled socket type.");
+ }
+
+ *instance = r;
+ return 0;
+}
+
+static void socket_close_fds(Socket *s) {
+ SocketPort *p;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ continue;
+
+ unit_unwatch_fd(UNIT(s), &p->fd_watch);
+ close_nointr_nofail(p->fd);
+
+ /* One little note: we should never delete any sockets
+ * in the file system here! After all some other
+ * process we spawned might still have a reference of
+ * this fd and wants to continue to use it. Therefore
+ * we delete sockets in the file system before we
+ * create a new one, not after we stopped using
+ * one! */
+
+ p->fd = -1;
+ }
+}
+
+static void socket_apply_socket_options(Socket *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ if (s->keep_alive) {
+ int b = s->keep_alive;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
+ log_warning("SO_KEEPALIVE failed: %m");
+ }
+
+ if (s->broadcast) {
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
+ log_warning("SO_BROADCAST failed: %m");
+ }
+
+ if (s->pass_cred) {
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+ log_warning("SO_PASSCRED failed: %m");
+ }
+
+ if (s->pass_sec) {
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
+ log_warning("SO_PASSSEC failed: %m");
+ }
+
+ if (s->priority >= 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
+ log_warning("SO_PRIORITY failed: %m");
+
+ if (s->receive_buffer > 0) {
+ int value = (int) s->receive_buffer;
+
+ /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
+ log_warning("SO_RCVBUF failed: %m");
+ }
+
+ if (s->send_buffer > 0) {
+ int value = (int) s->send_buffer;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
+ log_warning("SO_SNDBUF failed: %m");
+ }
+
+ if (s->mark >= 0)
+ if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
+ log_warning("SO_MARK failed: %m");
+
+ if (s->ip_tos >= 0)
+ if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
+ log_warning("IP_TOS failed: %m");
+
+ if (s->ip_ttl >= 0) {
+ int r, x;
+
+ r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
+
+ if (socket_ipv6_is_supported())
+ x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
+ else {
+ x = -1;
+ errno = EAFNOSUPPORT;
+ }
+
+ if (r < 0 && x < 0)
+ log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m");
+ }
+
+ if (s->tcp_congestion)
+ if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
+ log_warning("TCP_CONGESTION failed: %m");
+}
+
+static void socket_apply_fifo_options(Socket *s, int fd) {
+ assert(s);
+ assert(fd >= 0);
+
+ if (s->pipe_size > 0)
+ if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0)
+ log_warning("F_SETPIPE_SZ: %m");
+}
+
+static int fifo_address_create(
+ const char *path,
+ mode_t directory_mode,
+ mode_t socket_mode,
+ int *_fd) {
+
+ int fd = -1, r = 0;
+ struct stat st;
+ mode_t old_mask;
+
+ assert(path);
+ assert(_fd);
+
+ mkdir_parents(path, directory_mode);
+
+ if ((r = label_fifofile_set(path)) < 0)
+ goto fail;
+
+ /* Enforce the right access mode for the fifo */
+ old_mask = umask(~ socket_mode);
+
+ /* Include the original umask in our mask */
+ umask(~socket_mode | old_mask);
+
+ r = mkfifo(path, socket_mode);
+ umask(old_mask);
+
+ if (r < 0 && errno != EEXIST) {
+ r = -errno;
+ goto fail;
+ }
+
+ if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ label_file_clear();
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (!S_ISFIFO(st.st_mode) ||
+ (st.st_mode & 0777) != (socket_mode & ~old_mask) ||
+ st.st_uid != getuid() ||
+ st.st_gid != getgid()) {
+
+ r = -EEXIST;
+ goto fail;
+ }
+
+ *_fd = fd;
+ return 0;
+
+fail:
+ label_file_clear();
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+static int special_address_create(
+ const char *path,
+ int *_fd) {
+
+ int fd = -1, r = 0;
+ struct stat st;
+
+ assert(path);
+ assert(_fd);
+
+ if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ /* Check whether this is a /proc, /sys or /dev file or char device */
+ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ *_fd = fd;
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+static int mq_address_create(
+ const char *path,
+ mode_t mq_mode,
+ long maxmsg,
+ long msgsize,
+ int *_fd) {
+
+ int fd = -1, r = 0;
+ struct stat st;
+ mode_t old_mask;
+ struct mq_attr _attr, *attr = NULL;
+
+ assert(path);
+ assert(_fd);
+
+ if (maxmsg > 0 && msgsize > 0) {
+ zero(_attr);
+ _attr.mq_flags = O_NONBLOCK;
+ _attr.mq_maxmsg = maxmsg;
+ _attr.mq_msgsize = msgsize;
+ attr = &_attr;
+ }
+
+ /* Enforce the right access mode for the mq */
+ old_mask = umask(~ mq_mode);
+
+ /* Include the original umask in our mask */
+ umask(~mq_mode | old_mask);
+
+ fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
+ umask(old_mask);
+
+ if (fd < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if ((st.st_mode & 0777) != (mq_mode & ~old_mask) ||
+ st.st_uid != getuid() ||
+ st.st_gid != getgid()) {
+
+ r = -EEXIST;
+ goto fail;
+ }
+
+ *_fd = fd;
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+static int socket_open_fds(Socket *s) {
+ SocketPort *p;
+ int r;
+ char *label = NULL;
+ bool know_label = false;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+
+ if (p->fd >= 0)
+ continue;
+
+ if (p->type == SOCKET_SOCKET) {
+
+ if (!know_label) {
+
+ if ((r = socket_instantiate_service(s)) < 0)
+ return r;
+
+ if (UNIT_DEREF(s->service) &&
+ SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
+ r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
+
+ if (r < 0) {
+ if (r != -EPERM)
+ return r;
+ }
+ }
+
+ know_label = true;
+ }
+
+ if ((r = socket_address_listen(
+ &p->address,
+ s->backlog,
+ s->bind_ipv6_only,
+ s->bind_to_device,
+ s->free_bind,
+ s->transparent,
+ s->directory_mode,
+ s->socket_mode,
+ label,
+ &p->fd)) < 0)
+ goto rollback;
+
+ socket_apply_socket_options(s, p->fd);
+
+ } else if (p->type == SOCKET_SPECIAL) {
+
+ if ((r = special_address_create(
+ p->path,
+ &p->fd)) < 0)
+ goto rollback;
+
+ } else if (p->type == SOCKET_FIFO) {
+
+ if ((r = fifo_address_create(
+ p->path,
+ s->directory_mode,
+ s->socket_mode,
+ &p->fd)) < 0)
+ goto rollback;
+
+ socket_apply_fifo_options(s, p->fd);
+ } else if (p->type == SOCKET_MQUEUE) {
+
+ if ((r = mq_address_create(
+ p->path,
+ s->socket_mode,
+ s->mq_maxmsg,
+ s->mq_msgsize,
+ &p->fd)) < 0)
+ goto rollback;
+ } else
+ assert_not_reached("Unknown port type");
+ }
+
+ label_free(label);
+ return 0;
+
+rollback:
+ socket_close_fds(s);
+ label_free(label);
+ return r;
+}
+
+static void socket_unwatch_fds(Socket *s) {
+ SocketPort *p;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ continue;
+
+ unit_unwatch_fd(UNIT(s), &p->fd_watch);
+ }
+}
+
+static int socket_watch_fds(Socket *s) {
+ SocketPort *p;
+ int r;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ continue;
+
+ p->fd_watch.socket_accept =
+ s->accept &&
+ p->type == SOCKET_SOCKET &&
+ socket_address_can_accept(&p->address);
+
+ if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ socket_unwatch_fds(s);
+ return r;
+}
+
+static void socket_set_state(Socket *s, SocketState state) {
+ SocketState old_state;
+ assert(s);
+
+ old_state = s->state;
+ s->state = state;
+
+ if (state != SOCKET_START_PRE &&
+ state != SOCKET_START_POST &&
+ state != SOCKET_STOP_PRE &&
+ state != SOCKET_STOP_PRE_SIGTERM &&
+ state != SOCKET_STOP_PRE_SIGKILL &&
+ state != SOCKET_STOP_POST &&
+ state != SOCKET_FINAL_SIGTERM &&
+ state != SOCKET_FINAL_SIGKILL) {
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+ socket_unwatch_control_pid(s);
+ s->control_command = NULL;
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+ }
+
+ if (state != SOCKET_LISTENING)
+ socket_unwatch_fds(s);
+
+ if (state != SOCKET_START_POST &&
+ state != SOCKET_LISTENING &&
+ state != SOCKET_RUNNING &&
+ state != SOCKET_STOP_PRE &&
+ state != SOCKET_STOP_PRE_SIGTERM &&
+ state != SOCKET_STOP_PRE_SIGKILL)
+ socket_close_fds(s);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(s)->id,
+ socket_state_to_string(old_state),
+ socket_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int socket_coldplug(Unit *u) {
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(s);
+ assert(s->state == SOCKET_DEAD);
+
+ if (s->deserialized_state != s->state) {
+
+ if (s->deserialized_state == SOCKET_START_PRE ||
+ s->deserialized_state == SOCKET_START_POST ||
+ s->deserialized_state == SOCKET_STOP_PRE ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGKILL ||
+ s->deserialized_state == SOCKET_STOP_POST ||
+ s->deserialized_state == SOCKET_FINAL_SIGTERM ||
+ s->deserialized_state == SOCKET_FINAL_SIGKILL) {
+
+ if (s->control_pid <= 0)
+ return -EBADMSG;
+
+ if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ return r;
+ }
+
+ if (s->deserialized_state == SOCKET_START_POST ||
+ s->deserialized_state == SOCKET_LISTENING ||
+ s->deserialized_state == SOCKET_RUNNING ||
+ s->deserialized_state == SOCKET_STOP_PRE ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
+ s->deserialized_state == SOCKET_STOP_PRE_SIGKILL)
+ if ((r = socket_open_fds(s)) < 0)
+ return r;
+
+ if (s->deserialized_state == SOCKET_LISTENING)
+ if ((r = socket_watch_fds(s)) < 0)
+ return r;
+
+ socket_set_state(s, s->deserialized_state);
+ }
+
+ return 0;
+}
+
+static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
+ pid_t pid;
+ int r;
+ char **argv;
+
+ assert(s);
+ assert(c);
+ assert(_pid);
+
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+
+ if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = exec_spawn(c,
+ argv,
+ &s->exec_context,
+ NULL, 0,
+ UNIT(s)->manager->environment,
+ true,
+ true,
+ true,
+ UNIT(s)->manager->confirm_spawn,
+ UNIT(s)->cgroup_bondings,
+ UNIT(s)->cgroup_attributes,
+ &pid);
+
+ strv_free(argv);
+ if (r < 0)
+ goto fail;
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ goto fail;
+
+ *_pid = pid;
+
+ return 0;
+
+fail:
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ return r;
+}
+
+static void socket_enter_dead(Socket *s, SocketResult f) {
+ assert(s);
+
+ if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
+}
+
+static void socket_enter_signal(Socket *s, SocketState state, SocketResult f);
+
+static void socket_enter_stop_post(Socket *s, SocketResult f) {
+ int r;
+ assert(s);
+
+ if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ socket_unwatch_control_pid(s);
+
+ s->control_command_id = SOCKET_EXEC_STOP_POST;
+
+ if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) {
+ if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ socket_set_state(s, SOCKET_STOP_POST);
+ } else
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
+ int r;
+ Set *pid_set = NULL;
+ bool wait_for_exit = false;
+
+ assert(s);
+
+ if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ if (s->exec_context.kill_mode != KILL_NONE) {
+ int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
+
+ if (s->control_pid > 0) {
+ if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
+
+ log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
+ else
+ wait_for_exit = true;
+ }
+
+ if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (s->control_pid > 0)
+ if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
+ goto fail;
+
+ if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning("Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+
+ set_free(pid_set);
+ pid_set = NULL;
+ }
+ }
+
+ if (wait_for_exit) {
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+
+ socket_set_state(s, state);
+ } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
+ socket_enter_stop_post(s, SOCKET_SUCCESS);
+ else
+ socket_enter_dead(s, SOCKET_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+
+ if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
+ socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
+ else
+ socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
+
+ if (pid_set)
+ set_free(pid_set);
+}
+
+static void socket_enter_stop_pre(Socket *s, SocketResult f) {
+ int r;
+ assert(s);
+
+ if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ socket_unwatch_control_pid(s);
+
+ s->control_command_id = SOCKET_EXEC_STOP_PRE;
+
+ if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) {
+ if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ socket_set_state(s, SOCKET_STOP_PRE);
+ } else
+ socket_enter_stop_post(s, SOCKET_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r));
+ socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_enter_listening(Socket *s) {
+ int r;
+ assert(s);
+
+ r = socket_watch_fds(s);
+ if (r < 0) {
+ log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
+ goto fail;
+ }
+
+ socket_set_state(s, SOCKET_LISTENING);
+ return;
+
+fail:
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_enter_start_post(Socket *s) {
+ int r;
+ assert(s);
+
+ r = socket_open_fds(s);
+ if (r < 0) {
+ log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r));
+ goto fail;
+ }
+
+ socket_unwatch_control_pid(s);
+
+ s->control_command_id = SOCKET_EXEC_START_POST;
+
+ if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
+ r = socket_spawn(s, s->control_command, &s->control_pid);
+ if (r < 0) {
+ log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
+ goto fail;
+ }
+
+ socket_set_state(s, SOCKET_START_POST);
+ } else
+ socket_enter_listening(s);
+
+ return;
+
+fail:
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_enter_start_pre(Socket *s) {
+ int r;
+ assert(s);
+
+ socket_unwatch_control_pid(s);
+
+ s->control_command_id = SOCKET_EXEC_START_PRE;
+
+ if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) {
+ if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ socket_set_state(s, SOCKET_START_PRE);
+ } else
+ socket_enter_start_post(s);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
+ socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_enter_running(Socket *s, int cfd) {
+ int r;
+ DBusError error;
+
+ assert(s);
+ dbus_error_init(&error);
+
+ /* We don't take connections anymore if we are supposed to
+ * shut down anyway */
+ if (unit_pending_inactive(UNIT(s))) {
+ log_debug("Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
+
+ if (cfd >= 0)
+ close_nointr_nofail(cfd);
+ else {
+ /* Flush all sockets by closing and reopening them */
+ socket_close_fds(s);
+
+ r = socket_watch_fds(s);
+ if (r < 0) {
+ log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+ }
+ }
+
+ return;
+ }
+
+ if (cfd < 0) {
+ Iterator i;
+ Unit *u;
+ bool pending = false;
+
+ /* If there's already a start pending don't bother to
+ * do anything */
+ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
+ if (unit_pending_active(u)) {
+ pending = true;
+ break;
+ }
+
+ if (!pending) {
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
+ if (r < 0)
+ goto fail;
+ }
+
+ socket_set_state(s, SOCKET_RUNNING);
+ } else {
+ char *prefix, *instance = NULL, *name;
+ Service *service;
+
+ if (s->n_connections >= s->max_connections) {
+ log_warning("Too many incoming connections (%u)", s->n_connections);
+ close_nointr_nofail(cfd);
+ return;
+ }
+
+ r = socket_instantiate_service(s);
+ if (r < 0)
+ goto fail;
+
+ r = instance_from_socket(cfd, s->n_accepted, &instance);
+ if (r < 0) {
+ if (r != -ENOTCONN)
+ goto fail;
+
+ /* ENOTCONN is legitimate if TCP RST was received.
+ * This connection is over, but the socket unit lives on. */
+ close_nointr_nofail(cfd);
+ return;
+ }
+
+ prefix = unit_name_to_prefix(UNIT(s)->id);
+ if (!prefix) {
+ free(instance);
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ name = unit_name_build(prefix, instance, ".service");
+ free(prefix);
+ free(instance);
+
+ if (!name) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = unit_add_name(UNIT_DEREF(s->service), name);
+ if (r < 0) {
+ free(name);
+ goto fail;
+ }
+
+ service = SERVICE(UNIT_DEREF(s->service));
+ unit_ref_unset(&s->service);
+ s->n_accepted ++;
+
+ UNIT(service)->no_gc = false;
+
+ unit_choose_id(UNIT(service), name);
+ free(name);
+
+ r = service_set_socket_fd(service, cfd, s);
+ if (r < 0)
+ goto fail;
+
+ cfd = -1;
+ s->n_connections ++;
+
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
+ if (r < 0)
+ goto fail;
+
+ /* Notify clients about changed counters */
+ unit_add_to_dbus_queue(UNIT(s));
+ }
+
+ return;
+
+fail:
+ log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r));
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+
+ if (cfd >= 0)
+ close_nointr_nofail(cfd);
+
+ dbus_error_free(&error);
+}
+
+static void socket_run_next(Socket *s) {
+ int r;
+
+ assert(s);
+ assert(s->control_command);
+ assert(s->control_command->command_next);
+
+ socket_unwatch_control_pid(s);
+
+ s->control_command = s->control_command->command_next;
+
+ if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ return;
+
+fail:
+ log_warning("%s failed to run next task: %s", UNIT(s)->id, strerror(-r));
+
+ if (s->state == SOCKET_START_POST)
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+ else if (s->state == SOCKET_STOP_POST)
+ socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
+ else
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
+}
+
+static int socket_start(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ /* We cannot fulfill this request right now, try again later
+ * please! */
+ if (s->state == SOCKET_STOP_PRE ||
+ s->state == SOCKET_STOP_PRE_SIGKILL ||
+ s->state == SOCKET_STOP_PRE_SIGTERM ||
+ s->state == SOCKET_STOP_POST ||
+ s->state == SOCKET_FINAL_SIGTERM ||
+ s->state == SOCKET_FINAL_SIGKILL)
+ return -EAGAIN;
+
+ if (s->state == SOCKET_START_PRE ||
+ s->state == SOCKET_START_POST)
+ return 0;
+
+ /* Cannot run this without the service being around */
+ if (UNIT_DEREF(s->service)) {
+ Service *service;
+
+ service = SERVICE(UNIT_DEREF(s->service));
+
+ if (UNIT(service)->load_state != UNIT_LOADED) {
+ log_error("Socket service %s not loaded, refusing.", UNIT(service)->id);
+ return -ENOENT;
+ }
+
+ /* If the service is already active we cannot start the
+ * socket */
+ if (service->state != SERVICE_DEAD &&
+ service->state != SERVICE_FAILED &&
+ service->state != SERVICE_AUTO_RESTART) {
+ log_error("Socket service %s already active, refusing.", UNIT(service)->id);
+ return -EBUSY;
+ }
+
+#ifdef HAVE_SYSV_COMPAT
+ if (service->sysv_path) {
+ log_error("Using SysV services for socket activation is not supported. Refusing.");
+ return -ENOENT;
+ }
+#endif
+ }
+
+ assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
+
+ s->result = SOCKET_SUCCESS;
+ socket_enter_start_pre(s);
+ return 0;
+}
+
+static int socket_stop(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ /* Already on it */
+ if (s->state == SOCKET_STOP_PRE ||
+ s->state == SOCKET_STOP_PRE_SIGTERM ||
+ s->state == SOCKET_STOP_PRE_SIGKILL ||
+ s->state == SOCKET_STOP_POST ||
+ s->state == SOCKET_FINAL_SIGTERM ||
+ s->state == SOCKET_FINAL_SIGKILL)
+ return 0;
+
+ /* If there's already something running we go directly into
+ * kill mode. */
+ if (s->state == SOCKET_START_PRE ||
+ s->state == SOCKET_START_POST) {
+ socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS);
+ return -EAGAIN;
+ }
+
+ assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
+
+ socket_enter_stop_pre(s, SOCKET_SUCCESS);
+ return 0;
+}
+
+static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Socket *s = SOCKET(u);
+ SocketPort *p;
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
+ unit_serialize_item(u, f, "result", socket_result_to_string(s->result));
+ unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
+
+ if (s->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+
+ if (s->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
+
+ LIST_FOREACH(port, p, s->ports) {
+ int copy;
+
+ if (p->fd < 0)
+ continue;
+
+ if ((copy = fdset_put_dup(fds, p->fd)) < 0)
+ return copy;
+
+ if (p->type == SOCKET_SOCKET) {
+ char *t;
+
+ if ((r = socket_address_print(&p->address, &t)) < 0)
+ return r;
+
+ if (socket_address_family(&p->address) == AF_NETLINK)
+ unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t);
+ else
+ unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t);
+ free(t);
+ } else if (p->type == SOCKET_SPECIAL)
+ unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
+ else {
+ assert(p->type == SOCKET_FIFO);
+ unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
+ }
+ }
+
+ return 0;
+}
+
+static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Socket *s = SOCKET(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SocketState state;
+
+ if ((state = socket_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ SocketResult f;
+
+ f = socket_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ } else if (streq(key, "n-accepted")) {
+ unsigned k;
+
+ if (safe_atou(value, &k) < 0)
+ log_debug("Failed to parse n-accepted value %s", value);
+ else
+ s->n_accepted += k;
+ } else if (streq(key, "control-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ s->control_pid = pid;
+ } else if (streq(key, "control-command")) {
+ SocketExecCommand id;
+
+ if ((id = socket_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ s->control_command_id = id;
+ s->control_command = s->exec_command[id];
+ }
+ } else if (streq(key, "fifo")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse fifo value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (p->type == SOCKET_FIFO &&
+ streq_ptr(p->path, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else if (streq(key, "special")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse special value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (p->type == SOCKET_SPECIAL &&
+ streq_ptr(p->path, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else if (streq(key, "socket")) {
+ int fd, type, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse socket value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (socket_address_is(&p->address, value+skip, type))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else if (streq(key, "netlink")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse socket value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (socket_address_is_netlink(&p->address, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState socket_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SOCKET(u)->state];
+}
+
+static const char *socket_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return socket_state_to_string(SOCKET(u)->state);
+}
+
+static bool socket_check_gc(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(u);
+
+ return s->n_connections > 0;
+}
+
+static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
+ Socket *s = SOCKET(u);
+ int cfd = -1;
+
+ assert(s);
+ assert(fd >= 0);
+
+ if (s->state != SOCKET_LISTENING)
+ return;
+
+ log_debug("Incoming traffic on %s", u->id);
+
+ if (events != EPOLLIN) {
+
+ if (events & EPOLLHUP)
+ log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->id);
+ else
+ log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events);
+
+ goto fail;
+ }
+
+ if (w->socket_accept) {
+ for (;;) {
+
+ if ((cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("Failed to accept socket: %m");
+ goto fail;
+ }
+
+ break;
+ }
+
+ socket_apply_socket_options(s, cfd);
+ }
+
+ socket_enter_running(s, cfd);
+ return;
+
+fail:
+ socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
+}
+
+static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ Socket *s = SOCKET(u);
+ SocketResult f;
+
+ assert(s);
+ assert(pid >= 0);
+
+ if (pid != s->control_pid)
+ return;
+
+ s->control_pid = 0;
+
+ if (is_clean_exit(code, status))
+ f = SOCKET_SUCCESS;
+ else if (code == CLD_EXITED)
+ f = SOCKET_FAILURE_EXIT_CODE;
+ else if (code == CLD_KILLED)
+ f = SOCKET_FAILURE_SIGNAL;
+ else if (code == CLD_DUMPED)
+ f = SOCKET_FAILURE_CORE_DUMP;
+ else
+ assert_not_reached("Unknown code");
+
+ if (s->control_command) {
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
+
+ if (s->control_command->ignore)
+ f = SOCKET_SUCCESS;
+ }
+
+ log_full(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+
+ if (f != SOCKET_SUCCESS)
+ s->result = f;
+
+ if (s->control_command &&
+ s->control_command->command_next &&
+ f == SOCKET_SUCCESS) {
+
+ log_debug("%s running next command for state %s", u->id, socket_state_to_string(s->state));
+ socket_run_next(s);
+ } else {
+ s->control_command = NULL;
+ s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+
+ /* No further commands for this step, so let's figure
+ * out what to do next */
+
+ log_debug("%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state));
+
+ switch (s->state) {
+
+ case SOCKET_START_PRE:
+ if (f == SOCKET_SUCCESS)
+ socket_enter_start_post(s);
+ else
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f);
+ break;
+
+ case SOCKET_START_POST:
+ if (f == SOCKET_SUCCESS)
+ socket_enter_listening(s);
+ else
+ socket_enter_stop_pre(s, f);
+ break;
+
+ case SOCKET_STOP_PRE:
+ case SOCKET_STOP_PRE_SIGTERM:
+ case SOCKET_STOP_PRE_SIGKILL:
+ socket_enter_stop_post(s, f);
+ break;
+
+ case SOCKET_STOP_POST:
+ case SOCKET_FINAL_SIGTERM:
+ case SOCKET_FINAL_SIGKILL:
+ socket_enter_dead(s, f);
+ break;
+
+ default:
+ assert_not_reached("Uh, control process died at wrong time.");
+ }
+ }
+
+ /* Notify clients about changed exit status */
+ unit_add_to_dbus_queue(u);
+}
+
+static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+ assert(elapsed == 1);
+ assert(w == &s->timer_watch);
+
+ switch (s->state) {
+
+ case SOCKET_START_PRE:
+ log_warning("%s starting timed out. Terminating.", u->id);
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ case SOCKET_START_POST:
+ log_warning("%s starting timed out. Stopping.", u->id);
+ socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ case SOCKET_STOP_PRE:
+ log_warning("%s stopping timed out. Terminating.", u->id);
+ socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ case SOCKET_STOP_PRE_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out. Killing.", u->id);
+ socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->id);
+ socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case SOCKET_STOP_PRE_SIGKILL:
+ log_warning("%s still around after SIGKILL. Ignoring.", u->id);
+ socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ case SOCKET_STOP_POST:
+ log_warning("%s stopping timed out (2). Terminating.", u->id);
+ socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ case SOCKET_FINAL_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out (2). Killing.", u->id);
+ socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->id);
+ socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case SOCKET_FINAL_SIGKILL:
+ log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
+ socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
+ break;
+
+ default:
+ assert_not_reached("Timeout at wrong time.");
+ }
+}
+
+int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
+ int *rfds;
+ unsigned rn_fds, k;
+ SocketPort *p;
+
+ assert(s);
+ assert(fds);
+ assert(n_fds);
+
+ /* Called from the service code for requesting our fds */
+
+ rn_fds = 0;
+ LIST_FOREACH(port, p, s->ports)
+ if (p->fd >= 0)
+ rn_fds++;
+
+ if (rn_fds <= 0) {
+ *fds = NULL;
+ *n_fds = 0;
+ return 0;
+ }
+
+ if (!(rfds = new(int, rn_fds)))
+ return -ENOMEM;
+
+ k = 0;
+ LIST_FOREACH(port, p, s->ports)
+ if (p->fd >= 0)
+ rfds[k++] = p->fd;
+
+ assert(k == rn_fds);
+
+ *fds = rfds;
+ *n_fds = rn_fds;
+
+ return 0;
+}
+
+void socket_notify_service_dead(Socket *s, bool failed_permanent) {
+ assert(s);
+
+ /* The service is dead. Dang!
+ *
+ * This is strictly for one-instance-for-all-connections
+ * services. */
+
+ if (s->state == SOCKET_RUNNING) {
+ log_debug("%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent));
+ if (failed_permanent)
+ socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
+ else
+ socket_enter_listening(s);
+ }
+}
+
+void socket_connection_unref(Socket *s) {
+ assert(s);
+
+ /* The service is dead. Yay!
+ *
+ * This is strictly for one-instance-per-connection
+ * services. */
+
+ assert(s->n_connections > 0);
+ s->n_connections--;
+
+ log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections);
+}
+
+static void socket_reset_failed(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ if (s->state == SOCKET_FAILED)
+ socket_set_state(s, SOCKET_DEAD);
+
+ s->result = SOCKET_SUCCESS;
+}
+
+static int socket_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+ Socket *s = SOCKET(u);
+ int r = 0;
+ Set *pid_set = NULL;
+
+ assert(s);
+
+ if (who == KILL_MAIN) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes");
+ return -ESRCH;
+ }
+
+ if (s->control_pid <= 0 && who == KILL_CONTROL) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (s->control_pid > 0)
+ if (kill(s->control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
+ int q;
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (s->control_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
+ if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+finish:
+ if (pid_set)
+ set_free(pid_set);
+
+ return r;
+}
+
+static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
+ [SOCKET_DEAD] = "dead",
+ [SOCKET_START_PRE] = "start-pre",
+ [SOCKET_START_POST] = "start-post",
+ [SOCKET_LISTENING] = "listening",
+ [SOCKET_RUNNING] = "running",
+ [SOCKET_STOP_PRE] = "stop-pre",
+ [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
+ [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
+ [SOCKET_STOP_POST] = "stop-post",
+ [SOCKET_FINAL_SIGTERM] = "final-sigterm",
+ [SOCKET_FINAL_SIGKILL] = "final-sigkill",
+ [SOCKET_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
+
+static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
+ [SOCKET_EXEC_START_PRE] = "StartPre",
+ [SOCKET_EXEC_START_POST] = "StartPost",
+ [SOCKET_EXEC_STOP_PRE] = "StopPre",
+ [SOCKET_EXEC_STOP_POST] = "StopPost"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
+
+static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
+ [SOCKET_SUCCESS] = "success",
+ [SOCKET_FAILURE_RESOURCES] = "resources",
+ [SOCKET_FAILURE_TIMEOUT] = "timeout",
+ [SOCKET_FAILURE_EXIT_CODE] = "exit-code",
+ [SOCKET_FAILURE_SIGNAL] = "signal",
+ [SOCKET_FAILURE_CORE_DUMP] = "core-dump",
+ [SOCKET_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
+
+const UnitVTable socket_vtable = {
+ .suffix = ".socket",
+ .object_size = sizeof(Socket),
+ .sections =
+ "Unit\0"
+ "Socket\0"
+ "Install\0",
+
+ .init = socket_init,
+ .done = socket_done,
+ .load = socket_load,
+
+ .kill = socket_kill,
+
+ .coldplug = socket_coldplug,
+
+ .dump = socket_dump,
+
+ .start = socket_start,
+ .stop = socket_stop,
+
+ .serialize = socket_serialize,
+ .deserialize_item = socket_deserialize_item,
+
+ .active_state = socket_active_state,
+ .sub_state_to_string = socket_sub_state_to_string,
+
+ .check_gc = socket_check_gc,
+
+ .fd_event = socket_fd_event,
+ .sigchld_event = socket_sigchld_event,
+ .timer_event = socket_timer_event,
+
+ .reset_failed = socket_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Socket",
+ .bus_message_handler = bus_socket_message_handler,
+ .bus_invalidating_properties = bus_socket_invalidating_properties
+};
diff --git a/src/core/socket.h b/src/core/socket.h
new file mode 100644
index 0000000..6470d8b
--- /dev/null
+++ b/src/core/socket.h
@@ -0,0 +1,173 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosockethfoo
+#define foosockethfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Socket Socket;
+
+#include "manager.h"
+#include "unit.h"
+#include "socket-util.h"
+#include "mount.h"
+#include "service.h"
+
+typedef enum SocketState {
+ SOCKET_DEAD,
+ SOCKET_START_PRE,
+ SOCKET_START_POST,
+ SOCKET_LISTENING,
+ SOCKET_RUNNING,
+ SOCKET_STOP_PRE,
+ SOCKET_STOP_PRE_SIGTERM,
+ SOCKET_STOP_PRE_SIGKILL,
+ SOCKET_STOP_POST,
+ SOCKET_FINAL_SIGTERM,
+ SOCKET_FINAL_SIGKILL,
+ SOCKET_FAILED,
+ _SOCKET_STATE_MAX,
+ _SOCKET_STATE_INVALID = -1
+} SocketState;
+
+typedef enum SocketExecCommand {
+ SOCKET_EXEC_START_PRE,
+ SOCKET_EXEC_START_POST,
+ SOCKET_EXEC_STOP_PRE,
+ SOCKET_EXEC_STOP_POST,
+ _SOCKET_EXEC_COMMAND_MAX,
+ _SOCKET_EXEC_COMMAND_INVALID = -1
+} SocketExecCommand;
+
+typedef enum SocketType {
+ SOCKET_SOCKET,
+ SOCKET_FIFO,
+ SOCKET_SPECIAL,
+ SOCKET_MQUEUE,
+ _SOCKET_FIFO_MAX,
+ _SOCKET_FIFO_INVALID = -1
+} SocketType;
+
+typedef enum SocketResult {
+ SOCKET_SUCCESS,
+ SOCKET_FAILURE_RESOURCES,
+ SOCKET_FAILURE_TIMEOUT,
+ SOCKET_FAILURE_EXIT_CODE,
+ SOCKET_FAILURE_SIGNAL,
+ SOCKET_FAILURE_CORE_DUMP,
+ SOCKET_FAILURE_SERVICE_FAILED_PERMANENT,
+ _SOCKET_RESULT_MAX,
+ _SOCKET_RESULT_INVALID = -1
+} SocketResult;
+
+typedef struct SocketPort {
+ SocketType type;
+ int fd;
+
+ SocketAddress address;
+ char *path;
+ Watch fd_watch;
+
+ LIST_FIELDS(struct SocketPort, port);
+} SocketPort;
+
+struct Socket {
+ Unit meta;
+
+ LIST_HEAD(SocketPort, ports);
+
+ unsigned n_accepted;
+ unsigned n_connections;
+ unsigned max_connections;
+
+ unsigned backlog;
+ usec_t timeout_usec;
+
+ ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
+ ExecContext exec_context;
+
+ /* For Accept=no sockets refers to the one service we'll
+ activate. For Accept=yes sockets is either NULL, or filled
+ when the next service we spawn. */
+ UnitRef service;
+
+ SocketState state, deserialized_state;
+
+ Watch timer_watch;
+
+ ExecCommand* control_command;
+ SocketExecCommand control_command_id;
+ pid_t control_pid;
+
+ mode_t directory_mode;
+ mode_t socket_mode;
+
+ SocketResult result;
+
+ bool accept;
+
+ /* Socket options */
+ bool keep_alive;
+ bool free_bind;
+ bool transparent;
+ bool broadcast;
+ bool pass_cred;
+ bool pass_sec;
+ int priority;
+ int mark;
+ size_t receive_buffer;
+ size_t send_buffer;
+ int ip_tos;
+ int ip_ttl;
+ size_t pipe_size;
+ char *bind_to_device;
+ char *tcp_congestion;
+ long mq_maxmsg;
+ long mq_msgsize;
+
+ /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
+ SocketAddressBindIPv6Only bind_ipv6_only;
+};
+
+/* Called from the service code when collecting fds */
+int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
+
+/* Called from the service when it shut down */
+void socket_notify_service_dead(Socket *s, bool failed_permanent);
+
+/* Called from the mount code figure out if a mount is a dependency of
+ * any of the sockets of this socket */
+int socket_add_one_mount_link(Socket *s, Mount *m);
+
+/* Called from the service code when a per-connection service ended */
+void socket_connection_unref(Socket *s);
+
+extern const UnitVTable socket_vtable;
+
+const char* socket_state_to_string(SocketState i);
+SocketState socket_state_from_string(const char *s);
+
+const char* socket_exec_command_to_string(SocketExecCommand i);
+SocketExecCommand socket_exec_command_from_string(const char *s);
+
+const char* socket_result_to_string(SocketResult i);
+SocketResult socket_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/spawn-agent.h b/src/core/spawn-agent.h
new file mode 100644
index 0000000..fd0a910
--- /dev/null
+++ b/src/core/spawn-agent.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foospawnagenthfoo
+#define foospawnagenthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+void agent_open(void);
+void agent_close(void);
+
+#endif
diff --git a/src/core/special.h b/src/core/special.h
new file mode 100644
index 0000000..43e2e6f
--- /dev/null
+++ b/src/core/special.h
@@ -0,0 +1,88 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foospecialhfoo
+#define foospecialhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#define SPECIAL_DEFAULT_TARGET "default.target"
+
+/* Shutdown targets */
+#define SPECIAL_UMOUNT_TARGET "umount.target"
+/* This is not really intended to be started by directly. This is
+ * mostly so that other targets (reboot/halt/poweroff) can depend on
+ * it to bring all services down that want to be brought down on
+ * system shutdown. */
+#define SPECIAL_SHUTDOWN_TARGET "shutdown.target"
+#define SPECIAL_HALT_TARGET "halt.target"
+#define SPECIAL_POWEROFF_TARGET "poweroff.target"
+#define SPECIAL_REBOOT_TARGET "reboot.target"
+#define SPECIAL_KEXEC_TARGET "kexec.target"
+#define SPECIAL_EXIT_TARGET "exit.target"
+
+/* Special boot targets */
+#define SPECIAL_RESCUE_TARGET "rescue.target"
+#define SPECIAL_EMERGENCY_TARGET "emergency.target"
+
+/* Early boot targets */
+#define SPECIAL_SYSINIT_TARGET "sysinit.target"
+#define SPECIAL_SOCKETS_TARGET "sockets.target"
+#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */
+#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
+#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
+#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
+#define SPECIAL_SWAP_TARGET "swap.target"
+#define SPECIAL_BASIC_TARGET "basic.target"
+
+/* LSB compatibility */
+#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
+#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
+#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
+#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */
+#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
+#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */
+#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */
+#define SPECIAL_HTTP_DAEMON_TARGET "http-daemon.target"
+
+/* Magic early boot services */
+#define SPECIAL_FSCK_SERVICE "fsck at .service"
+#define SPECIAL_QUOTACHECK_SERVICE "quotacheck.service"
+#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
+#define SPECIAL_REMOUNT_ROOTFS_SERVICE "remount-rootfs.service"
+
+/* Services systemd relies on */
+#define SPECIAL_DBUS_SERVICE "dbus.service"
+#define SPECIAL_DBUS_SOCKET "dbus.socket"
+#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
+#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
+
+/* Magic init signals */
+#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
+#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
+#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
+
+/* For SysV compatibility. Usually an alias for a saner target. On
+ * SysV-free systems this doesn't exist. */
+#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target"
+#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
+#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
+#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
+
+#endif
diff --git a/src/core/swap.c b/src/core/swap.c
new file mode 100644
index 0000000..9c72732
--- /dev/null
+++ b/src/core/swap.c
@@ -0,0 +1,1415 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/swap.h>
+#include <libudev.h>
+
+#include "unit.h"
+#include "swap.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "unit-name.h"
+#include "dbus-swap.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "exit-status.h"
+#include "def.h"
+
+static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
+ [SWAP_DEAD] = UNIT_INACTIVE,
+ [SWAP_ACTIVATING] = UNIT_ACTIVATING,
+ [SWAP_ACTIVE] = UNIT_ACTIVE,
+ [SWAP_DEACTIVATING] = UNIT_DEACTIVATING,
+ [SWAP_ACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
+ [SWAP_ACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
+ [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
+ [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
+ [SWAP_FAILED] = UNIT_FAILED
+};
+
+static void swap_unset_proc_swaps(Swap *s) {
+ Swap *first;
+
+ assert(s);
+
+ if (!s->parameters_proc_swaps.what)
+ return;
+
+ /* Remove this unit from the chain of swaps which share the
+ * same kernel swap device. */
+
+ first = hashmap_get(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
+ LIST_REMOVE(Swap, same_proc_swaps, first, s);
+
+ if (first)
+ hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first);
+ else
+ hashmap_remove(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
+
+ free(s->parameters_proc_swaps.what);
+ s->parameters_proc_swaps.what = NULL;
+}
+
+static void swap_init(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+ assert(UNIT(s)->load_state == UNIT_STUB);
+
+ s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+
+ exec_context_init(&s->exec_context);
+ s->exec_context.std_output = u->manager->default_std_output;
+ s->exec_context.std_error = u->manager->default_std_error;
+
+ s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
+
+ s->timer_watch.type = WATCH_INVALID;
+
+ s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
+
+ UNIT(s)->ignore_on_isolate = true;
+}
+
+static void swap_unwatch_control_pid(Swap *s) {
+ assert(s);
+
+ if (s->control_pid <= 0)
+ return;
+
+ unit_unwatch_pid(UNIT(s), s->control_pid);
+ s->control_pid = 0;
+}
+
+static void swap_done(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ swap_unset_proc_swaps(s);
+
+ free(s->what);
+ s->what = NULL;
+
+ free(s->parameters_etc_fstab.what);
+ free(s->parameters_fragment.what);
+ s->parameters_etc_fstab.what = s->parameters_fragment.what = NULL;
+
+ exec_context_done(&s->exec_context);
+ exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
+ s->control_command = NULL;
+
+ swap_unwatch_control_pid(s);
+
+ unit_unwatch_timer(u, &s->timer_watch);
+}
+
+int swap_add_one_mount_link(Swap *s, Mount *m) {
+ int r;
+
+ assert(s);
+ assert(m);
+
+ if (UNIT(s)->load_state != UNIT_LOADED ||
+ UNIT(m)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (is_device_path(s->what))
+ return 0;
+
+ if (!path_startswith(s->what, m->where))
+ return 0;
+
+ if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ return r;
+
+ return 0;
+}
+
+static int swap_add_mount_links(Swap *s) {
+ Unit *other;
+ int r;
+
+ assert(s);
+
+ LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
+ if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0)
+ return r;
+
+ return 0;
+}
+
+static int swap_add_target_links(Swap *s) {
+ Unit *tu;
+ SwapParameters *p;
+ int r;
+
+ assert(s);
+
+ if (s->from_fragment)
+ p = &s->parameters_fragment;
+ else if (s->from_etc_fstab)
+ p = &s->parameters_etc_fstab;
+ else
+ return 0;
+
+ if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0)
+ return r;
+
+ if (!p->noauto &&
+ !p->nofail &&
+ (p->handle || UNIT(s)->manager->swap_auto) &&
+ s->from_etc_fstab &&
+ UNIT(s)->manager->running_as == MANAGER_SYSTEM)
+ if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0)
+ return r;
+
+ return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true);
+}
+
+static int swap_add_device_links(Swap *s) {
+ SwapParameters *p;
+
+ assert(s);
+
+ if (!s->what)
+ return 0;
+
+ if (s->from_fragment)
+ p = &s->parameters_fragment;
+ else if (s->from_etc_fstab)
+ p = &s->parameters_etc_fstab;
+ else
+ return 0;
+
+ if (is_device_path(s->what))
+ return unit_add_node_link(UNIT(s), s->what,
+ !p->noauto && p->nofail &&
+ UNIT(s)->manager->running_as == MANAGER_SYSTEM);
+ else
+ /* File based swap devices need to be ordered after
+ * remount-rootfs.service, since they might need a
+ * writable file system. */
+ return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_ROOTFS_SERVICE, NULL, true);
+}
+
+static int swap_add_default_dependencies(Swap *s) {
+ int r;
+
+ assert(s);
+
+ if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int swap_verify(Swap *s) {
+ bool b;
+ char *e;
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!(e = unit_name_from_path(s->what, ".swap")))
+ return -ENOMEM;
+
+ b = unit_has_name(UNIT(s), e);
+ free(e);
+
+ if (!b) {
+ log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
+ log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int swap_load(Unit *u) {
+ int r;
+ Swap *s = SWAP(u);
+
+ assert(s);
+ assert(u->load_state == UNIT_STUB);
+
+ /* Load a .swap file */
+ if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_LOADED) {
+ if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
+ return r;
+
+ if (UNIT(s)->fragment_path)
+ s->from_fragment = true;
+
+ if (!s->what) {
+ if (s->parameters_fragment.what)
+ s->what = strdup(s->parameters_fragment.what);
+ else if (s->parameters_etc_fstab.what)
+ s->what = strdup(s->parameters_etc_fstab.what);
+ else if (s->parameters_proc_swaps.what)
+ s->what = strdup(s->parameters_proc_swaps.what);
+ else
+ s->what = unit_name_to_path(u->id);
+
+ if (!s->what)
+ return -ENOMEM;
+ }
+
+ path_kill_slashes(s->what);
+
+ if (!UNIT(s)->description)
+ if ((r = unit_set_description(u, s->what)) < 0)
+ return r;
+
+ if ((r = swap_add_device_links(s)) < 0)
+ return r;
+
+ if ((r = swap_add_mount_links(s)) < 0)
+ return r;
+
+ if ((r = swap_add_target_links(s)) < 0)
+ return r;
+
+ if ((r = unit_add_default_cgroups(u)) < 0)
+ return r;
+
+ if (UNIT(s)->default_dependencies)
+ if ((r = swap_add_default_dependencies(s)) < 0)
+ return r;
+ }
+
+ return swap_verify(s);
+}
+
+int swap_add_one(
+ Manager *m,
+ const char *what,
+ const char *what_proc_swaps,
+ int priority,
+ bool noauto,
+ bool nofail,
+ bool handle,
+ bool set_flags) {
+
+ Unit *u = NULL;
+ char *e = NULL, *wp = NULL;
+ bool delete = false;
+ int r;
+ SwapParameters *p;
+
+ assert(m);
+ assert(what);
+
+ e = unit_name_from_path(what, ".swap");
+ if (!e)
+ return -ENOMEM;
+
+ u = manager_get_unit(m, e);
+
+ if (what_proc_swaps &&
+ u &&
+ SWAP(u)->from_proc_swaps &&
+ !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
+ return -EEXIST;
+
+ if (!u) {
+ delete = true;
+
+ u = unit_new(m, sizeof(Swap));
+ if (!u) {
+ free(e);
+ return -ENOMEM;
+ }
+
+ r = unit_add_name(u, e);
+ if (r < 0)
+ goto fail;
+
+ SWAP(u)->what = strdup(what);
+ if (!SWAP(u)->what) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ unit_add_to_load_queue(u);
+ } else
+ delete = false;
+
+ if (what_proc_swaps) {
+ Swap *first;
+
+ p = &SWAP(u)->parameters_proc_swaps;
+
+ if (!p->what) {
+ if (!(wp = strdup(what_proc_swaps))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!m->swaps_by_proc_swaps)
+ if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(p->what);
+ p->what = wp;
+
+ first = hashmap_get(m->swaps_by_proc_swaps, wp);
+ LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u));
+
+ if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0)
+ goto fail;
+ }
+
+ if (set_flags) {
+ SWAP(u)->is_active = true;
+ SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps;
+ }
+
+ SWAP(u)->from_proc_swaps = true;
+
+ } else {
+ p = &SWAP(u)->parameters_etc_fstab;
+
+ if (!(wp = strdup(what))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ free(p->what);
+ p->what = wp;
+
+ SWAP(u)->from_etc_fstab = true;
+ }
+
+ p->priority = priority;
+ p->noauto = noauto;
+ p->nofail = nofail;
+ p->handle = handle;
+
+ unit_add_to_dbus_queue(u);
+
+ free(e);
+
+ return 0;
+
+fail:
+ log_warning("Failed to load swap unit: %s", strerror(-r));
+
+ free(wp);
+ free(e);
+
+ if (delete && u)
+ unit_free(u);
+
+ return r;
+}
+
+static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
+ struct stat st;
+ int r = 0, k;
+
+ assert(m);
+
+ if (stat(device, &st) >= 0 && S_ISBLK(st.st_mode)) {
+ struct udev_device *d;
+ const char *dn;
+ struct udev_list_entry *item = NULL, *first = NULL;
+
+ /* So this is a proper swap device. Create swap units
+ * for all names this swap device is known under */
+
+ if (!(d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev)))
+ return -ENOMEM;
+
+ if ((dn = udev_device_get_devnode(d)))
+ r = swap_add_one(m, dn, device, prio, false, false, false, set_flags);
+
+ /* Add additional units for all symlinks */
+ first = udev_device_get_devlinks_list_entry(d);
+ udev_list_entry_foreach(item, first) {
+ const char *p;
+
+ /* Don't bother with the /dev/block links */
+ p = udev_list_entry_get_name(item);
+
+ if (path_startswith(p, "/dev/block/"))
+ continue;
+
+ if (stat(p, &st) >= 0)
+ if ((!S_ISBLK(st.st_mode)) || st.st_rdev != udev_device_get_devnum(d))
+ continue;
+
+ if ((k = swap_add_one(m, p, device, prio, false, false, false, set_flags)) < 0)
+ r = k;
+ }
+
+ udev_device_unref(d);
+ }
+
+ if ((k = swap_add_one(m, device, device, prio, false, false, false, set_flags)) < 0)
+ r = k;
+
+ return r;
+}
+
+static void swap_set_state(Swap *s, SwapState state) {
+ SwapState old_state;
+
+ assert(s);
+
+ old_state = s->state;
+ s->state = state;
+
+ if (state != SWAP_ACTIVATING &&
+ state != SWAP_ACTIVATING_SIGTERM &&
+ state != SWAP_ACTIVATING_SIGKILL &&
+ state != SWAP_DEACTIVATING &&
+ state != SWAP_DEACTIVATING_SIGTERM &&
+ state != SWAP_DEACTIVATING_SIGKILL) {
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+ swap_unwatch_control_pid(s);
+ s->control_command = NULL;
+ s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
+ }
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(s)->id,
+ swap_state_to_string(old_state),
+ swap_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int swap_coldplug(Unit *u) {
+ Swap *s = SWAP(u);
+ SwapState new_state = SWAP_DEAD;
+ int r;
+
+ assert(s);
+ assert(s->state == SWAP_DEAD);
+
+ if (s->deserialized_state != s->state)
+ new_state = s->deserialized_state;
+ else if (s->from_proc_swaps)
+ new_state = SWAP_ACTIVE;
+
+ if (new_state != s->state) {
+
+ if (new_state == SWAP_ACTIVATING ||
+ new_state == SWAP_ACTIVATING_SIGTERM ||
+ new_state == SWAP_ACTIVATING_SIGKILL ||
+ new_state == SWAP_DEACTIVATING ||
+ new_state == SWAP_DEACTIVATING_SIGTERM ||
+ new_state == SWAP_DEACTIVATING_SIGKILL) {
+
+ if (s->control_pid <= 0)
+ return -EBADMSG;
+
+ if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ return r;
+ }
+
+ swap_set_state(s, new_state);
+ }
+
+ return 0;
+}
+
+static void swap_dump(Unit *u, FILE *f, const char *prefix) {
+ Swap *s = SWAP(u);
+ SwapParameters *p;
+
+ assert(s);
+ assert(f);
+
+ if (s->from_proc_swaps)
+ p = &s->parameters_proc_swaps;
+ else if (s->from_fragment)
+ p = &s->parameters_fragment;
+ else
+ p = &s->parameters_etc_fstab;
+
+ fprintf(f,
+ "%sSwap State: %s\n"
+ "%sResult: %s\n"
+ "%sWhat: %s\n"
+ "%sPriority: %i\n"
+ "%sNoAuto: %s\n"
+ "%sNoFail: %s\n"
+ "%sHandle: %s\n"
+ "%sFrom /etc/fstab: %s\n"
+ "%sFrom /proc/swaps: %s\n"
+ "%sFrom fragment: %s\n",
+ prefix, swap_state_to_string(s->state),
+ prefix, swap_result_to_string(s->result),
+ prefix, s->what,
+ prefix, p->priority,
+ prefix, yes_no(p->noauto),
+ prefix, yes_no(p->nofail),
+ prefix, yes_no(p->handle),
+ prefix, yes_no(s->from_etc_fstab),
+ prefix, yes_no(s->from_proc_swaps),
+ prefix, yes_no(s->from_fragment));
+
+ if (s->control_pid > 0)
+ fprintf(f,
+ "%sControl PID: %lu\n",
+ prefix, (unsigned long) s->control_pid);
+
+ exec_context_dump(&s->exec_context, f, prefix);
+}
+
+static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(c);
+ assert(_pid);
+
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+
+ if ((r = exec_spawn(c,
+ NULL,
+ &s->exec_context,
+ NULL, 0,
+ UNIT(s)->manager->environment,
+ true,
+ true,
+ true,
+ UNIT(s)->manager->confirm_spawn,
+ UNIT(s)->cgroup_bondings,
+ UNIT(s)->cgroup_attributes,
+ &pid)) < 0)
+ goto fail;
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ goto fail;
+
+ *_pid = pid;
+
+ return 0;
+
+fail:
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ return r;
+}
+
+static void swap_enter_dead(Swap *s, SwapResult f) {
+ assert(s);
+
+ if (f != SWAP_SUCCESS)
+ s->result = f;
+
+ swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
+}
+
+static void swap_enter_active(Swap *s, SwapResult f) {
+ assert(s);
+
+ if (f != SWAP_SUCCESS)
+ s->result = f;
+
+ swap_set_state(s, SWAP_ACTIVE);
+}
+
+static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
+ int r;
+ Set *pid_set = NULL;
+ bool wait_for_exit = false;
+
+ assert(s);
+
+ if (f != SWAP_SUCCESS)
+ s->result = f;
+
+ if (s->exec_context.kill_mode != KILL_NONE) {
+ int sig = (state == SWAP_ACTIVATING_SIGTERM ||
+ state == SWAP_DEACTIVATING_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
+
+ if (s->control_pid > 0) {
+ if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
+
+ log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
+ else
+ wait_for_exit = true;
+ }
+
+ if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (s->control_pid > 0)
+ if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
+ goto fail;
+
+ if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning("Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+
+ set_free(pid_set);
+ pid_set = NULL;
+ }
+ }
+
+ if (wait_for_exit) {
+ if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ goto fail;
+
+ swap_set_state(s, state);
+ } else
+ swap_enter_dead(s, SWAP_SUCCESS);
+
+ return;
+
+fail:
+ log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+
+ swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
+
+ if (pid_set)
+ set_free(pid_set);
+}
+
+static void swap_enter_activating(Swap *s) {
+ int r, priority;
+
+ assert(s);
+
+ s->control_command_id = SWAP_EXEC_ACTIVATE;
+ s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
+
+ if (s->from_fragment)
+ priority = s->parameters_fragment.priority;
+ else if (s->from_etc_fstab)
+ priority = s->parameters_etc_fstab.priority;
+ else
+ priority = -1;
+
+ if (priority >= 0) {
+ char p[LINE_MAX];
+
+ snprintf(p, sizeof(p), "%i", priority);
+ char_array_0(p);
+
+ r = exec_command_set(
+ s->control_command,
+ "/sbin/swapon",
+ "-p",
+ p,
+ s->what,
+ NULL);
+ } else
+ r = exec_command_set(
+ s->control_command,
+ "/sbin/swapon",
+ s->what,
+ NULL);
+
+ if (r < 0)
+ goto fail;
+
+ swap_unwatch_control_pid(s);
+
+ if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ swap_set_state(s, SWAP_ACTIVATING);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r));
+ swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
+}
+
+static void swap_enter_deactivating(Swap *s) {
+ int r;
+
+ assert(s);
+
+ s->control_command_id = SWAP_EXEC_DEACTIVATE;
+ s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE;
+
+ if ((r = exec_command_set(
+ s->control_command,
+ "/sbin/swapoff",
+ s->what,
+ NULL)) < 0)
+ goto fail;
+
+ swap_unwatch_control_pid(s);
+
+ if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
+ goto fail;
+
+ swap_set_state(s, SWAP_DEACTIVATING);
+
+ return;
+
+fail:
+ log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r));
+ swap_enter_active(s, SWAP_FAILURE_RESOURCES);
+}
+
+static int swap_start(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ /* We cannot fulfill this request right now, try again later
+ * please! */
+
+ if (s->state == SWAP_DEACTIVATING ||
+ s->state == SWAP_DEACTIVATING_SIGTERM ||
+ s->state == SWAP_DEACTIVATING_SIGKILL ||
+ s->state == SWAP_ACTIVATING_SIGTERM ||
+ s->state == SWAP_ACTIVATING_SIGKILL)
+ return -EAGAIN;
+
+ if (s->state == SWAP_ACTIVATING)
+ return 0;
+
+ assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED);
+
+ s->result = SWAP_SUCCESS;
+ swap_enter_activating(s);
+ return 0;
+}
+
+static int swap_stop(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ if (s->state == SWAP_DEACTIVATING ||
+ s->state == SWAP_DEACTIVATING_SIGTERM ||
+ s->state == SWAP_DEACTIVATING_SIGKILL ||
+ s->state == SWAP_ACTIVATING_SIGTERM ||
+ s->state == SWAP_ACTIVATING_SIGKILL)
+ return 0;
+
+ assert(s->state == SWAP_ACTIVATING ||
+ s->state == SWAP_ACTIVE);
+
+ swap_enter_deactivating(s);
+ return 0;
+}
+
+static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", swap_state_to_string(s->state));
+ unit_serialize_item(u, f, "result", swap_result_to_string(s->result));
+
+ if (s->control_pid > 0)
+ unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+
+ if (s->control_command_id >= 0)
+ unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
+
+ return 0;
+}
+
+static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SwapState state;
+
+ if ((state = swap_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ SwapResult f;
+
+ f = swap_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != SWAP_SUCCESS)
+ s->result = f;
+ } else if (streq(key, "control-pid")) {
+ pid_t pid;
+
+ if (parse_pid(value, &pid) < 0)
+ log_debug("Failed to parse control-pid value %s", value);
+ else
+ s->control_pid = pid;
+
+ } else if (streq(key, "control-command")) {
+ SwapExecCommand id;
+
+ if ((id = swap_exec_command_from_string(value)) < 0)
+ log_debug("Failed to parse exec-command value %s", value);
+ else {
+ s->control_command_id = id;
+ s->control_command = s->exec_command + id;
+ }
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState swap_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SWAP(u)->state];
+}
+
+static const char *swap_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return swap_state_to_string(SWAP(u)->state);
+}
+
+static bool swap_check_gc(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ return s->from_etc_fstab || s->from_proc_swaps;
+}
+
+static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
+ Swap *s = SWAP(u);
+ SwapResult f;
+
+ assert(s);
+ assert(pid >= 0);
+
+ if (pid != s->control_pid)
+ return;
+
+ s->control_pid = 0;
+
+ if (is_clean_exit(code, status))
+ f = SWAP_SUCCESS;
+ else if (code == CLD_EXITED)
+ f = SWAP_FAILURE_EXIT_CODE;
+ else if (code == CLD_KILLED)
+ f = SWAP_FAILURE_SIGNAL;
+ else if (code == CLD_DUMPED)
+ f = SWAP_FAILURE_CORE_DUMP;
+ else
+ assert_not_reached("Unknown code");
+
+ if (f != SWAP_SUCCESS)
+ s->result = f;
+
+ if (s->control_command) {
+ exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
+
+ s->control_command = NULL;
+ s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
+ }
+
+ log_full(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+
+ switch (s->state) {
+
+ case SWAP_ACTIVATING:
+ case SWAP_ACTIVATING_SIGTERM:
+ case SWAP_ACTIVATING_SIGKILL:
+
+ if (f == SWAP_SUCCESS)
+ swap_enter_active(s, f);
+ else
+ swap_enter_dead(s, f);
+ break;
+
+ case SWAP_DEACTIVATING:
+ case SWAP_DEACTIVATING_SIGKILL:
+ case SWAP_DEACTIVATING_SIGTERM:
+
+ if (f == SWAP_SUCCESS)
+ swap_enter_dead(s, f);
+ else
+ swap_enter_dead(s, f);
+ break;
+
+ default:
+ assert_not_reached("Uh, control process died at wrong time.");
+ }
+
+ /* Notify clients about changed exit status */
+ unit_add_to_dbus_queue(u);
+
+ /* Request a reload of /proc/swaps, so that following units
+ * can follow our state change */
+ u->manager->request_reload = true;
+}
+
+static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+ assert(elapsed == 1);
+ assert(w == &s->timer_watch);
+
+ switch (s->state) {
+
+ case SWAP_ACTIVATING:
+ log_warning("%s activation timed out. Stopping.", u->id);
+ swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
+ break;
+
+ case SWAP_DEACTIVATING:
+ log_warning("%s deactivation timed out. Stopping.", u->id);
+ swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
+ break;
+
+ case SWAP_ACTIVATING_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s activation timed out. Killing.", u->id);
+ swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->id);
+ swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case SWAP_DEACTIVATING_SIGTERM:
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s deactivation timed out. Killing.", u->id);
+ swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
+ } else {
+ log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id);
+ swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
+ }
+ break;
+
+ case SWAP_ACTIVATING_SIGKILL:
+ case SWAP_DEACTIVATING_SIGKILL:
+ log_warning("%s swap process still around after SIGKILL. Ignoring.", u->id);
+ swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
+ break;
+
+ default:
+ assert_not_reached("Timeout at wrong time.");
+ }
+}
+
+static int swap_load_proc_swaps(Manager *m, bool set_flags) {
+ unsigned i;
+ int r = 0;
+
+ assert(m);
+
+ rewind(m->proc_swaps);
+
+ (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+ for (i = 1;; i++) {
+ char *dev = NULL, *d;
+ int prio = 0, k;
+
+ if ((k = fscanf(m->proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%i\n", /* priority */
+ &dev, &prio)) != 2) {
+
+ if (k == EOF)
+ break;
+
+ log_warning("Failed to parse /proc/swaps:%u.", i);
+ free(dev);
+ continue;
+ }
+
+ d = cunescape(dev);
+ free(dev);
+
+ if (!d)
+ return -ENOMEM;
+
+ k = swap_process_new_swap(m, d, prio, set_flags);
+ free(d);
+
+ if (k < 0)
+ r = k;
+ }
+
+ return r;
+}
+
+int swap_dispatch_reload(Manager *m) {
+ /* This function should go as soon as the kernel properly notifies us */
+
+ if (_likely_(!m->request_reload))
+ return 0;
+
+ m->request_reload = false;
+
+ return swap_fd_event(m, EPOLLPRI);
+}
+
+int swap_fd_event(Manager *m, int events) {
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(events & EPOLLPRI);
+
+ if ((r = swap_load_proc_swaps(m, true)) < 0) {
+ log_error("Failed to reread /proc/swaps: %s", strerror(-r));
+
+ /* Reset flags, just in case, for late calls */
+ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) {
+ Swap *swap = SWAP(u);
+
+ swap->is_active = swap->just_activated = false;
+ }
+
+ return 0;
+ }
+
+ manager_dispatch_load_queue(m);
+
+ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) {
+ Swap *swap = SWAP(u);
+
+ if (!swap->is_active) {
+ /* This has just been deactivated */
+
+ swap->from_proc_swaps = false;
+ swap_unset_proc_swaps(swap);
+
+ switch (swap->state) {
+
+ case SWAP_ACTIVE:
+ swap_enter_dead(swap, SWAP_SUCCESS);
+ break;
+
+ default:
+ swap_set_state(swap, swap->state);
+ break;
+ }
+
+ } else if (swap->just_activated) {
+
+ /* New swap entry */
+
+ switch (swap->state) {
+
+ case SWAP_DEAD:
+ case SWAP_FAILED:
+ swap_enter_active(swap, SWAP_SUCCESS);
+ break;
+
+ default:
+ /* Nothing really changed, but let's
+ * issue an notification call
+ * nonetheless, in case somebody is
+ * waiting for this. */
+ swap_set_state(swap, swap->state);
+ break;
+ }
+ }
+
+ /* Reset the flags for later calls */
+ swap->is_active = swap->just_activated = false;
+ }
+
+ return 1;
+}
+
+static Unit *swap_following(Unit *u) {
+ Swap *s = SWAP(u);
+ Swap *other, *first = NULL;
+
+ assert(s);
+
+ if (streq_ptr(s->what, s->parameters_proc_swaps.what))
+ return NULL;
+
+ /* Make everybody follow the unit that's named after the swap
+ * device in the kernel */
+
+ LIST_FOREACH_AFTER(same_proc_swaps, other, s)
+ if (streq_ptr(other->what, other->parameters_proc_swaps.what))
+ return UNIT(other);
+
+ LIST_FOREACH_BEFORE(same_proc_swaps, other, s) {
+ if (streq_ptr(other->what, other->parameters_proc_swaps.what))
+ return UNIT(other);
+
+ first = other;
+ }
+
+ return UNIT(first);
+}
+
+static int swap_following_set(Unit *u, Set **_set) {
+ Swap *s = SWAP(u);
+ Swap *other;
+ Set *set;
+ int r;
+
+ assert(s);
+ assert(_set);
+
+ if (LIST_JUST_US(same_proc_swaps, s)) {
+ *_set = NULL;
+ return 0;
+ }
+
+ if (!(set = set_new(NULL, NULL)))
+ return -ENOMEM;
+
+ LIST_FOREACH_AFTER(same_proc_swaps, other, s)
+ if ((r = set_put(set, other)) < 0)
+ goto fail;
+
+ LIST_FOREACH_BEFORE(same_proc_swaps, other, s)
+ if ((r = set_put(set, other)) < 0)
+ goto fail;
+
+ *_set = set;
+ return 1;
+
+fail:
+ set_free(set);
+ return r;
+}
+
+static void swap_shutdown(Manager *m) {
+ assert(m);
+
+ if (m->proc_swaps) {
+ fclose(m->proc_swaps);
+ m->proc_swaps = NULL;
+ }
+
+ hashmap_free(m->swaps_by_proc_swaps);
+ m->swaps_by_proc_swaps = NULL;
+}
+
+static int swap_enumerate(Manager *m) {
+ int r;
+ struct epoll_event ev;
+ assert(m);
+
+ if (!m->proc_swaps) {
+ if (!(m->proc_swaps = fopen("/proc/swaps", "re")))
+ return (errno == ENOENT) ? 0 : -errno;
+
+ m->swap_watch.type = WATCH_SWAP;
+ m->swap_watch.fd = fileno(m->proc_swaps);
+
+ zero(ev);
+ ev.events = EPOLLPRI;
+ ev.data.ptr = &m->swap_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0)
+ return -errno;
+ }
+
+ /* We rely on mount.c to load /etc/fstab for us */
+
+ if ((r = swap_load_proc_swaps(m, false)) < 0)
+ swap_shutdown(m);
+
+ return r;
+}
+
+static void swap_reset_failed(Unit *u) {
+ Swap *s = SWAP(u);
+
+ assert(s);
+
+ if (s->state == SWAP_FAILED)
+ swap_set_state(s, SWAP_DEAD);
+
+ s->result = SWAP_SUCCESS;
+}
+
+static int swap_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+ Swap *s = SWAP(u);
+ int r = 0;
+ Set *pid_set = NULL;
+
+ assert(s);
+
+ if (who == KILL_MAIN) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes");
+ return -ESRCH;
+ }
+
+ if (s->control_pid <= 0 && who == KILL_CONTROL) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (s->control_pid > 0)
+ if (kill(s->control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
+ int q;
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ return -ENOMEM;
+
+ /* Exclude the control pid from being killed via the cgroup */
+ if (s->control_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
+ if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+finish:
+ if (pid_set)
+ set_free(pid_set);
+
+ return r;
+}
+
+static const char* const swap_state_table[_SWAP_STATE_MAX] = {
+ [SWAP_DEAD] = "dead",
+ [SWAP_ACTIVATING] = "activating",
+ [SWAP_ACTIVE] = "active",
+ [SWAP_DEACTIVATING] = "deactivating",
+ [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
+ [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
+ [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
+ [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
+ [SWAP_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
+
+static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
+ [SWAP_EXEC_ACTIVATE] = "ExecActivate",
+ [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand);
+
+static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
+ [SWAP_SUCCESS] = "success",
+ [SWAP_FAILURE_RESOURCES] = "resources",
+ [SWAP_FAILURE_TIMEOUT] = "timeout",
+ [SWAP_FAILURE_EXIT_CODE] = "exit-code",
+ [SWAP_FAILURE_SIGNAL] = "signal",
+ [SWAP_FAILURE_CORE_DUMP] = "core-dump"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
+
+const UnitVTable swap_vtable = {
+ .suffix = ".swap",
+ .object_size = sizeof(Swap),
+ .sections =
+ "Unit\0"
+ "Swap\0"
+ "Install\0",
+
+ .no_alias = true,
+ .no_instances = true,
+ .show_status = true,
+
+ .init = swap_init,
+ .load = swap_load,
+ .done = swap_done,
+
+ .coldplug = swap_coldplug,
+
+ .dump = swap_dump,
+
+ .start = swap_start,
+ .stop = swap_stop,
+
+ .kill = swap_kill,
+
+ .serialize = swap_serialize,
+ .deserialize_item = swap_deserialize_item,
+
+ .active_state = swap_active_state,
+ .sub_state_to_string = swap_sub_state_to_string,
+
+ .check_gc = swap_check_gc,
+
+ .sigchld_event = swap_sigchld_event,
+ .timer_event = swap_timer_event,
+
+ .reset_failed = swap_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Swap",
+ .bus_message_handler = bus_swap_message_handler,
+ .bus_invalidating_properties = bus_swap_invalidating_properties,
+
+ .following = swap_following,
+ .following_set = swap_following_set,
+
+ .enumerate = swap_enumerate,
+ .shutdown = swap_shutdown
+};
diff --git a/src/core/swap.h b/src/core/swap.h
new file mode 100644
index 0000000..62d08da
--- /dev/null
+++ b/src/core/swap.h
@@ -0,0 +1,128 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooswaphfoo
+#define fooswaphfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2010 Maarten Lankhorst
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Swap Swap;
+
+#include "unit.h"
+
+typedef enum SwapState {
+ SWAP_DEAD,
+ SWAP_ACTIVATING,
+ SWAP_ACTIVE,
+ SWAP_DEACTIVATING,
+ SWAP_ACTIVATING_SIGTERM,
+ SWAP_ACTIVATING_SIGKILL,
+ SWAP_DEACTIVATING_SIGTERM,
+ SWAP_DEACTIVATING_SIGKILL,
+ SWAP_FAILED,
+ _SWAP_STATE_MAX,
+ _SWAP_STATE_INVALID = -1
+} SwapState;
+
+typedef enum SwapExecCommand {
+ SWAP_EXEC_ACTIVATE,
+ SWAP_EXEC_DEACTIVATE,
+ _SWAP_EXEC_COMMAND_MAX,
+ _SWAP_EXEC_COMMAND_INVALID = -1
+} SwapExecCommand;
+
+typedef struct SwapParameters {
+ char *what;
+ int priority;
+ bool noauto:1;
+ bool nofail:1;
+ bool handle:1;
+} SwapParameters;
+
+typedef enum SwapResult {
+ SWAP_SUCCESS,
+ SWAP_FAILURE_RESOURCES,
+ SWAP_FAILURE_TIMEOUT,
+ SWAP_FAILURE_EXIT_CODE,
+ SWAP_FAILURE_SIGNAL,
+ SWAP_FAILURE_CORE_DUMP,
+ _SWAP_RESULT_MAX,
+ _SWAP_RESULT_INVALID = -1
+} SwapResult;
+
+struct Swap {
+ Unit meta;
+
+ char *what;
+
+ SwapParameters parameters_etc_fstab;
+ SwapParameters parameters_proc_swaps;
+ SwapParameters parameters_fragment;
+
+ bool from_etc_fstab:1;
+ bool from_proc_swaps:1;
+ bool from_fragment:1;
+
+ /* Used while looking for swaps that vanished or got added
+ * from/to /proc/swaps */
+ bool is_active:1;
+ bool just_activated:1;
+
+ SwapResult result;
+
+ usec_t timeout_usec;
+
+ ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX];
+ ExecContext exec_context;
+
+ SwapState state, deserialized_state;
+
+ ExecCommand* control_command;
+ SwapExecCommand control_command_id;
+ pid_t control_pid;
+
+ Watch timer_watch;
+
+ /* In order to be able to distinguish dependencies on
+ different device nodes we might end up creating multiple
+ devices for the same swap. We chain them up here. */
+
+ LIST_FIELDS(struct Swap, same_proc_swaps);
+};
+
+extern const UnitVTable swap_vtable;
+
+int swap_add_one(Manager *m, const char *what, const char *what_proc_swaps, int prio, bool no_auto, bool no_fail, bool handle, bool set_flags);
+
+int swap_add_one_mount_link(Swap *s, Mount *m);
+
+int swap_dispatch_reload(Manager *m);
+int swap_fd_event(Manager *m, int events);
+
+const char* swap_state_to_string(SwapState i);
+SwapState swap_state_from_string(const char *s);
+
+const char* swap_exec_command_to_string(SwapExecCommand i);
+SwapExecCommand swap_exec_command_from_string(const char *s);
+
+const char* swap_result_to_string(SwapResult i);
+SwapResult swap_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/sysfs-show.h b/src/core/sysfs-show.h
new file mode 100644
index 0000000..9939e8b
--- /dev/null
+++ b/src/core/sysfs-show.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosysfsshowhfoo
+#define foosysfsshowhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int show_sysfs(const char *seat, const char *prefix, unsigned columns);
+
+#endif
diff --git a/src/core/target.c b/src/core/target.c
new file mode 100644
index 0000000..6c1e0c3
--- /dev/null
+++ b/src/core/target.c
@@ -0,0 +1,224 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "unit.h"
+#include "target.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "dbus-target.h"
+#include "special.h"
+#include "unit-name.h"
+
+static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
+ [TARGET_DEAD] = UNIT_INACTIVE,
+ [TARGET_ACTIVE] = UNIT_ACTIVE
+};
+
+static void target_set_state(Target *t, TargetState state) {
+ TargetState old_state;
+ assert(t);
+
+ old_state = t->state;
+ t->state = state;
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(t)->id,
+ target_state_to_string(old_state),
+ target_state_to_string(state));
+
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int target_add_default_dependencies(Target *t) {
+ static const UnitDependency deps[] = {
+ UNIT_REQUIRES,
+ UNIT_REQUIRES_OVERRIDABLE,
+ UNIT_REQUISITE,
+ UNIT_REQUISITE_OVERRIDABLE,
+ UNIT_WANTS,
+ UNIT_BIND_TO
+ };
+
+ Iterator i;
+ Unit *other;
+ int r;
+ unsigned k;
+
+ assert(t);
+
+ /* Imply ordering for requirement dependencies on target
+ * units. Note that when the user created a contradicting
+ * ordering manually we won't add anything in here to make
+ * sure we don't create a loop. */
+
+ for (k = 0; k < ELEMENTSOF(deps); k++)
+ SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i)
+ if ((r = unit_add_default_target_dependency(other, UNIT(t))) < 0)
+ return r;
+
+ /* Make sure targets are unloaded on shutdown */
+ return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+}
+
+static int target_load(Unit *u) {
+ Target *t = TARGET(u);
+ int r;
+
+ assert(t);
+
+ if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ return r;
+
+ /* This is a new unit? Then let's add in some extras */
+ if (u->load_state == UNIT_LOADED) {
+ if (u->default_dependencies)
+ if ((r = target_add_default_dependencies(t)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int target_coldplug(Unit *u) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(t->state == TARGET_DEAD);
+
+ if (t->deserialized_state != t->state)
+ target_set_state(t, t->deserialized_state);
+
+ return 0;
+}
+
+static void target_dump(Unit *u, FILE *f, const char *prefix) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(f);
+
+ fprintf(f,
+ "%sTarget State: %s\n",
+ prefix, target_state_to_string(t->state));
+}
+
+static int target_start(Unit *u) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(t->state == TARGET_DEAD);
+
+ target_set_state(t, TARGET_ACTIVE);
+ return 0;
+}
+
+static int target_stop(Unit *u) {
+ Target *t = TARGET(u);
+
+ assert(t);
+ assert(t->state == TARGET_ACTIVE);
+
+ target_set_state(t, TARGET_DEAD);
+ return 0;
+}
+
+static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Target *s = TARGET(u);
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", target_state_to_string(s->state));
+ return 0;
+}
+
+static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Target *s = TARGET(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ TargetState state;
+
+ if ((state = target_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState target_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[TARGET(u)->state];
+}
+
+static const char *target_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return target_state_to_string(TARGET(u)->state);
+}
+
+static const char* const target_state_table[_TARGET_STATE_MAX] = {
+ [TARGET_DEAD] = "dead",
+ [TARGET_ACTIVE] = "active"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
+
+const UnitVTable target_vtable = {
+ .suffix = ".target",
+ .object_size = sizeof(Target),
+ .sections =
+ "Unit\0"
+ "Target\0"
+ "Install\0",
+
+ .load = target_load,
+ .coldplug = target_coldplug,
+
+ .dump = target_dump,
+
+ .start = target_start,
+ .stop = target_stop,
+
+ .serialize = target_serialize,
+ .deserialize_item = target_deserialize_item,
+
+ .active_state = target_active_state,
+ .sub_state_to_string = target_sub_state_to_string,
+
+ .bus_interface = "org.freedesktop.systemd1.Target",
+ .bus_message_handler = bus_target_message_handler
+};
diff --git a/src/core/target.h b/src/core/target.h
new file mode 100644
index 0000000..5b97c86
--- /dev/null
+++ b/src/core/target.h
@@ -0,0 +1,47 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef footargethfoo
+#define footargethfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Target Target;
+
+#include "unit.h"
+
+typedef enum TargetState {
+ TARGET_DEAD,
+ TARGET_ACTIVE,
+ _TARGET_STATE_MAX,
+ _TARGET_STATE_INVALID = -1
+} TargetState;
+
+struct Target {
+ Unit meta;
+
+ TargetState state, deserialized_state;
+};
+
+extern const UnitVTable target_vtable;
+
+const char* target_state_to_string(TargetState i);
+TargetState target_state_from_string(const char *s);
+
+#endif
diff --git a/src/core/tcpwrap.c b/src/core/tcpwrap.c
new file mode 100644
index 0000000..0aab142
--- /dev/null
+++ b/src/core/tcpwrap.c
@@ -0,0 +1,68 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include "tcpwrap.h"
+#include "log.h"
+
+bool socket_tcpwrap(int fd, const char *name) {
+#ifdef HAVE_LIBWRAP
+ struct request_info req;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ struct sockaddr_un un;
+ struct sockaddr_storage storage;
+ } sa_union;
+ socklen_t l = sizeof(sa_union);
+
+ if (getsockname(fd, &sa_union.sa, &l) < 0)
+ return true;
+
+ if (sa_union.sa.sa_family != AF_INET &&
+ sa_union.sa.sa_family != AF_INET6)
+ return true;
+
+ request_init(&req,
+ RQ_DAEMON, name,
+ RQ_FILE, fd,
+ NULL);
+
+ fromhost(&req);
+
+ if (!hosts_access(&req)) {
+ log_warning("Connection refused by tcpwrap.");
+ return false;
+ }
+
+ log_debug("Connection accepted by tcpwrap.");
+#endif
+ return true;
+}
diff --git a/src/core/tcpwrap.h b/src/core/tcpwrap.h
new file mode 100644
index 0000000..4d4553e
--- /dev/null
+++ b/src/core/tcpwrap.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foolibwraphfoo
+#define foolibwraphfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+bool socket_tcpwrap(int fd, const char *name);
+
+#endif
diff --git a/src/core/timer.c b/src/core/timer.c
new file mode 100644
index 0000000..e318fee
--- /dev/null
+++ b/src/core/timer.c
@@ -0,0 +1,520 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "unit.h"
+#include "unit-name.h"
+#include "timer.h"
+#include "dbus-timer.h"
+#include "special.h"
+#include "bus-errors.h"
+
+static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
+ [TIMER_DEAD] = UNIT_INACTIVE,
+ [TIMER_WAITING] = UNIT_ACTIVE,
+ [TIMER_RUNNING] = UNIT_ACTIVE,
+ [TIMER_ELAPSED] = UNIT_ACTIVE,
+ [TIMER_FAILED] = UNIT_FAILED
+};
+
+static void timer_init(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ t->next_elapse = (usec_t) -1;
+}
+
+static void timer_done(Unit *u) {
+ Timer *t = TIMER(u);
+ TimerValue *v;
+
+ assert(t);
+
+ while ((v = t->values)) {
+ LIST_REMOVE(TimerValue, value, t->values, v);
+ free(v);
+ }
+
+ unit_unwatch_timer(u, &t->timer_watch);
+
+ unit_ref_unset(&t->unit);
+}
+
+static int timer_verify(Timer *t) {
+ assert(t);
+
+ if (UNIT(t)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (!t->values) {
+ log_error("%s lacks value setting. Refusing.", UNIT(t)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int timer_add_default_dependencies(Timer *t) {
+ int r;
+
+ assert(t);
+
+ if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
+ if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ return r;
+
+ if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ return r;
+ }
+
+ return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+}
+
+static int timer_load(Unit *u) {
+ Timer *t = TIMER(u);
+ int r;
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_LOADED) {
+
+ if (!UNIT_DEREF(t->unit)) {
+ Unit *x;
+
+ r = unit_load_related_unit(u, ".service", &x);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&t->unit, x);
+ }
+
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
+ if (r < 0)
+ return r;
+
+ if (UNIT(t)->default_dependencies)
+ if ((r = timer_add_default_dependencies(t)) < 0)
+ return r;
+ }
+
+ return timer_verify(t);
+}
+
+static void timer_dump(Unit *u, FILE *f, const char *prefix) {
+ Timer *t = TIMER(u);
+ TimerValue *v;
+ char
+ timespan1[FORMAT_TIMESPAN_MAX];
+
+ fprintf(f,
+ "%sTimer State: %s\n"
+ "%sResult: %s\n"
+ "%sUnit: %s\n",
+ prefix, timer_state_to_string(t->state),
+ prefix, timer_result_to_string(t->result),
+ prefix, UNIT_DEREF(t->unit)->id);
+
+ LIST_FOREACH(value, v, t->values)
+ fprintf(f,
+ "%s%s: %s\n",
+ prefix,
+ timer_base_to_string(v->base),
+ strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
+}
+
+static void timer_set_state(Timer *t, TimerState state) {
+ TimerState old_state;
+ assert(t);
+
+ old_state = t->state;
+ t->state = state;
+
+ if (state != TIMER_WAITING)
+ unit_unwatch_timer(UNIT(t), &t->timer_watch);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(t)->id,
+ timer_state_to_string(old_state),
+ timer_state_to_string(state));
+
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static void timer_enter_waiting(Timer *t, bool initial);
+
+static int timer_coldplug(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+ assert(t->state == TIMER_DEAD);
+
+ if (t->deserialized_state != t->state) {
+
+ if (t->deserialized_state == TIMER_WAITING)
+ timer_enter_waiting(t, false);
+ else
+ timer_set_state(t, t->deserialized_state);
+ }
+
+ return 0;
+}
+
+static void timer_enter_dead(Timer *t, TimerResult f) {
+ assert(t);
+
+ if (f != TIMER_SUCCESS)
+ t->result = f;
+
+ timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
+}
+
+static void timer_enter_waiting(Timer *t, bool initial) {
+ TimerValue *v;
+ usec_t base = 0, delay, n;
+ bool found = false;
+ int r;
+
+ n = now(CLOCK_MONOTONIC);
+
+ LIST_FOREACH(value, v, t->values) {
+
+ if (v->disabled)
+ continue;
+
+ switch (v->base) {
+
+ case TIMER_ACTIVE:
+ if (state_translation_table[t->state] == UNIT_ACTIVE)
+ base = UNIT(t)->inactive_exit_timestamp.monotonic;
+ else
+ base = n;
+ break;
+
+ case TIMER_BOOT:
+ /* CLOCK_MONOTONIC equals the uptime on Linux */
+ base = 0;
+ break;
+
+ case TIMER_STARTUP:
+ base = UNIT(t)->manager->startup_timestamp.monotonic;
+ break;
+
+ case TIMER_UNIT_ACTIVE:
+
+ if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
+ continue;
+
+ base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
+ break;
+
+ case TIMER_UNIT_INACTIVE:
+
+ if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
+ continue;
+
+ base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
+ break;
+
+ default:
+ assert_not_reached("Unknown timer base");
+ }
+
+ v->next_elapse = base + v->value;
+
+ if (!initial && v->next_elapse < n) {
+ v->disabled = true;
+ continue;
+ }
+
+ if (!found)
+ t->next_elapse = v->next_elapse;
+ else
+ t->next_elapse = MIN(t->next_elapse, v->next_elapse);
+
+ found = true;
+ }
+
+ if (!found) {
+ timer_set_state(t, TIMER_ELAPSED);
+ return;
+ }
+
+ delay = n < t->next_elapse ? t->next_elapse - n : 0;
+
+ if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0)
+ goto fail;
+
+ timer_set_state(t, TIMER_WAITING);
+ return;
+
+fail:
+ log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
+ timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
+}
+
+static void timer_enter_running(Timer *t) {
+ DBusError error;
+ int r;
+
+ assert(t);
+ dbus_error_init(&error);
+
+ /* Don't start job if we are supposed to go down */
+ if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
+ return;
+
+ if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0)
+ goto fail;
+
+ timer_set_state(t, TIMER_RUNNING);
+ return;
+
+fail:
+ log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
+ timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
+
+ dbus_error_free(&error);
+}
+
+static int timer_start(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+ assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
+
+ if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
+ return -ENOENT;
+
+ t->result = TIMER_SUCCESS;
+ timer_enter_waiting(t, true);
+ return 0;
+}
+
+static int timer_stop(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+ assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
+
+ timer_enter_dead(t, TIMER_SUCCESS);
+ return 0;
+}
+
+static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Timer *t = TIMER(u);
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
+ unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
+
+ return 0;
+}
+
+static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Timer *t = TIMER(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ TimerState state;
+
+ if ((state = timer_state_from_string(value)) < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ t->deserialized_state = state;
+ } else if (streq(key, "result")) {
+ TimerResult f;
+
+ f = timer_result_from_string(value);
+ if (f < 0)
+ log_debug("Failed to parse result value %s", value);
+ else if (f != TIMER_SUCCESS)
+ t->result = f;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static UnitActiveState timer_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[TIMER(u)->state];
+}
+
+static const char *timer_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return timer_state_to_string(TIMER(u)->state);
+}
+
+static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+ assert(elapsed == 1);
+
+ if (t->state != TIMER_WAITING)
+ return;
+
+ log_debug("Timer elapsed on %s", u->id);
+ timer_enter_running(t);
+}
+
+void timer_unit_notify(Unit *u, UnitActiveState new_state) {
+ Iterator i;
+ Unit *k;
+
+ if (u->type == UNIT_TIMER)
+ return;
+
+ SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
+ Timer *t;
+ TimerValue *v;
+
+ if (k->type != UNIT_TIMER)
+ continue;
+
+ if (k->load_state != UNIT_LOADED)
+ continue;
+
+ t = TIMER(k);
+
+ /* Reenable all timers that depend on unit state */
+ LIST_FOREACH(value, v, t->values)
+ if (v->base == TIMER_UNIT_ACTIVE ||
+ v->base == TIMER_UNIT_INACTIVE)
+ v->disabled = false;
+
+ switch (t->state) {
+
+ case TIMER_WAITING:
+ case TIMER_ELAPSED:
+
+ /* Recalculate sleep time */
+ timer_enter_waiting(t, false);
+ break;
+
+ case TIMER_RUNNING:
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
+ log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
+ timer_enter_waiting(t, false);
+ }
+
+ break;
+
+ case TIMER_DEAD:
+ case TIMER_FAILED:
+ break;
+
+ default:
+ assert_not_reached("Unknown timer state");
+ }
+ }
+}
+
+static void timer_reset_failed(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+
+ if (t->state == TIMER_FAILED)
+ timer_set_state(t, TIMER_DEAD);
+
+ t->result = TIMER_SUCCESS;
+}
+
+static const char* const timer_state_table[_TIMER_STATE_MAX] = {
+ [TIMER_DEAD] = "dead",
+ [TIMER_WAITING] = "waiting",
+ [TIMER_RUNNING] = "running",
+ [TIMER_ELAPSED] = "elapsed",
+ [TIMER_FAILED] = "failed"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
+
+static const char* const timer_base_table[_TIMER_BASE_MAX] = {
+ [TIMER_ACTIVE] = "OnActiveSec",
+ [TIMER_BOOT] = "OnBootSec",
+ [TIMER_STARTUP] = "OnStartupSec",
+ [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
+ [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
+
+static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
+ [TIMER_SUCCESS] = "success",
+ [TIMER_FAILURE_RESOURCES] = "resources"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
+
+const UnitVTable timer_vtable = {
+ .suffix = ".timer",
+ .object_size = sizeof(Timer),
+ .sections =
+ "Unit\0"
+ "Timer\0"
+ "Install\0",
+
+ .init = timer_init,
+ .done = timer_done,
+ .load = timer_load,
+
+ .coldplug = timer_coldplug,
+
+ .dump = timer_dump,
+
+ .start = timer_start,
+ .stop = timer_stop,
+
+ .serialize = timer_serialize,
+ .deserialize_item = timer_deserialize_item,
+
+ .active_state = timer_active_state,
+ .sub_state_to_string = timer_sub_state_to_string,
+
+ .timer_event = timer_timer_event,
+
+ .reset_failed = timer_reset_failed,
+
+ .bus_interface = "org.freedesktop.systemd1.Timer",
+ .bus_message_handler = bus_timer_message_handler,
+ .bus_invalidating_properties = bus_timer_invalidating_properties
+};
diff --git a/src/core/timer.h b/src/core/timer.h
new file mode 100644
index 0000000..f5c5c64
--- /dev/null
+++ b/src/core/timer.h
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef footimerhfoo
+#define footimerhfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Timer Timer;
+
+#include "unit.h"
+
+typedef enum TimerState {
+ TIMER_DEAD,
+ TIMER_WAITING,
+ TIMER_RUNNING,
+ TIMER_ELAPSED,
+ TIMER_FAILED,
+ _TIMER_STATE_MAX,
+ _TIMER_STATE_INVALID = -1
+} TimerState;
+
+typedef enum TimerBase {
+ TIMER_ACTIVE,
+ TIMER_BOOT,
+ TIMER_STARTUP,
+ TIMER_UNIT_ACTIVE,
+ TIMER_UNIT_INACTIVE,
+ _TIMER_BASE_MAX,
+ _TIMER_BASE_INVALID = -1
+} TimerBase;
+
+typedef struct TimerValue {
+ usec_t value;
+ usec_t next_elapse;
+
+ LIST_FIELDS(struct TimerValue, value);
+
+ TimerBase base;
+ bool disabled;
+} TimerValue;
+
+typedef enum TimerResult {
+ TIMER_SUCCESS,
+ TIMER_FAILURE_RESOURCES,
+ _TIMER_RESULT_MAX,
+ _TIMER_RESULT_INVALID = -1
+} TimerResult;
+
+struct Timer {
+ Unit meta;
+
+ LIST_HEAD(TimerValue, values);
+ usec_t next_elapse;
+
+ TimerState state, deserialized_state;
+ UnitRef unit;
+
+ Watch timer_watch;
+
+ TimerResult result;
+};
+
+void timer_unit_notify(Unit *u, UnitActiveState new_state);
+
+extern const UnitVTable timer_vtable;
+
+const char *timer_state_to_string(TimerState i);
+TimerState timer_state_from_string(const char *s);
+
+const char *timer_base_to_string(TimerBase i);
+TimerBase timer_base_from_string(const char *s);
+
+const char* timer_result_to_string(TimerResult i);
+TimerResult timer_result_from_string(const char *s);
+
+#endif
diff --git a/src/core/unit.c b/src/core/unit.c
new file mode 100644
index 0000000..9e33701
--- /dev/null
+++ b/src/core/unit.c
@@ -0,0 +1,2676 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "set.h"
+#include "unit.h"
+#include "macro.h"
+#include "strv.h"
+#include "load-fragment.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "unit-name.h"
+#include "specifier.h"
+#include "dbus-unit.h"
+#include "special.h"
+#include "cgroup-util.h"
+#include "missing.h"
+#include "cgroup-attr.h"
+
+const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
+ [UNIT_SERVICE] = &service_vtable,
+ [UNIT_TIMER] = &timer_vtable,
+ [UNIT_SOCKET] = &socket_vtable,
+ [UNIT_TARGET] = &target_vtable,
+ [UNIT_DEVICE] = &device_vtable,
+ [UNIT_MOUNT] = &mount_vtable,
+ [UNIT_AUTOMOUNT] = &automount_vtable,
+ [UNIT_SNAPSHOT] = &snapshot_vtable,
+ [UNIT_SWAP] = &swap_vtable,
+ [UNIT_PATH] = &path_vtable
+};
+
+Unit *unit_new(Manager *m, size_t size) {
+ Unit *u;
+
+ assert(m);
+ assert(size >= sizeof(Unit));
+
+ u = malloc0(size);
+ if (!u)
+ return NULL;
+
+ u->names = set_new(string_hash_func, string_compare_func);
+ if (!u->names) {
+ free(u);
+ return NULL;
+ }
+
+ u->manager = m;
+ u->type = _UNIT_TYPE_INVALID;
+ u->deserialized_job = _JOB_TYPE_INVALID;
+ u->default_dependencies = true;
+ u->unit_file_state = _UNIT_FILE_STATE_INVALID;
+
+ return u;
+}
+
+bool unit_has_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ return !!set_get(u->names, (char*) name);
+}
+
+int unit_add_name(Unit *u, const char *text) {
+ UnitType t;
+ char *s, *i = NULL;
+ int r;
+
+ assert(u);
+ assert(text);
+
+ if (unit_name_is_template(text)) {
+ if (!u->instance)
+ return -EINVAL;
+
+ s = unit_name_replace_instance(text, u->instance);
+ } else
+ s = strdup(text);
+
+ if (!s)
+ return -ENOMEM;
+
+ if (!unit_name_is_valid(s, false)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ assert_se((t = unit_name_to_type(s)) >= 0);
+
+ if (u->type != _UNIT_TYPE_INVALID && t != u->type) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if ((r = unit_name_to_instance(s, &i)) < 0)
+ goto fail;
+
+ if (i && unit_vtable[t]->no_instances) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Ensure that this unit is either instanced or not instanced,
+ * but not both. */
+ if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (unit_vtable[t]->no_alias &&
+ !set_isempty(u->names) &&
+ !set_get(u->names, s)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) {
+ r = -E2BIG;
+ goto fail;
+ }
+
+ if ((r = set_put(u->names, s)) < 0) {
+ if (r == -EEXIST)
+ r = 0;
+ goto fail;
+ }
+
+ if ((r = hashmap_put(u->manager->units, s, u)) < 0) {
+ set_remove(u->names, s);
+ goto fail;
+ }
+
+ if (u->type == _UNIT_TYPE_INVALID) {
+
+ u->type = t;
+ u->id = s;
+ u->instance = i;
+
+ LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u);
+
+ if (UNIT_VTABLE(u)->init)
+ UNIT_VTABLE(u)->init(u);
+ } else
+ free(i);
+
+ unit_add_to_dbus_queue(u);
+ return 0;
+
+fail:
+ free(s);
+ free(i);
+
+ return r;
+}
+
+int unit_choose_id(Unit *u, const char *name) {
+ char *s, *t = NULL, *i;
+ int r;
+
+ assert(u);
+ assert(name);
+
+ if (unit_name_is_template(name)) {
+
+ if (!u->instance)
+ return -EINVAL;
+
+ if (!(t = unit_name_replace_instance(name, u->instance)))
+ return -ENOMEM;
+
+ name = t;
+ }
+
+ /* Selects one of the names of this unit as the id */
+ s = set_get(u->names, (char*) name);
+ free(t);
+
+ if (!s)
+ return -ENOENT;
+
+ if ((r = unit_name_to_instance(s, &i)) < 0)
+ return r;
+
+ u->id = s;
+
+ free(u->instance);
+ u->instance = i;
+
+ unit_add_to_dbus_queue(u);
+
+ return 0;
+}
+
+int unit_set_description(Unit *u, const char *description) {
+ char *s;
+
+ assert(u);
+
+ if (!(s = strdup(description)))
+ return -ENOMEM;
+
+ free(u->description);
+ u->description = s;
+
+ unit_add_to_dbus_queue(u);
+ return 0;
+}
+
+bool unit_check_gc(Unit *u) {
+ assert(u);
+
+ if (u->load_state == UNIT_STUB)
+ return true;
+
+ if (UNIT_VTABLE(u)->no_gc)
+ return true;
+
+ if (u->no_gc)
+ return true;
+
+ if (u->job)
+ return true;
+
+ if (unit_active_state(u) != UNIT_INACTIVE)
+ return true;
+
+ if (UNIT_VTABLE(u)->check_gc)
+ if (UNIT_VTABLE(u)->check_gc(u))
+ return true;
+
+ return false;
+}
+
+void unit_add_to_load_queue(Unit *u) {
+ assert(u);
+ assert(u->type != _UNIT_TYPE_INVALID);
+
+ if (u->load_state != UNIT_STUB || u->in_load_queue)
+ return;
+
+ LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u);
+ u->in_load_queue = true;
+}
+
+void unit_add_to_cleanup_queue(Unit *u) {
+ assert(u);
+
+ if (u->in_cleanup_queue)
+ return;
+
+ LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u);
+ u->in_cleanup_queue = true;
+}
+
+void unit_add_to_gc_queue(Unit *u) {
+ assert(u);
+
+ if (u->in_gc_queue || u->in_cleanup_queue)
+ return;
+
+ if (unit_check_gc(u))
+ return;
+
+ LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u);
+ u->in_gc_queue = true;
+
+ u->manager->n_in_gc_queue ++;
+
+ if (u->manager->gc_queue_timestamp <= 0)
+ u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC);
+}
+
+void unit_add_to_dbus_queue(Unit *u) {
+ assert(u);
+ assert(u->type != _UNIT_TYPE_INVALID);
+
+ if (u->load_state == UNIT_STUB || u->in_dbus_queue)
+ return;
+
+ /* Shortcut things if nobody cares */
+ if (!bus_has_subscriber(u->manager)) {
+ u->sent_dbus_new_signal = true;
+ return;
+ }
+
+ LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
+ u->in_dbus_queue = true;
+}
+
+static void bidi_set_free(Unit *u, Set *s) {
+ Iterator i;
+ Unit *other;
+
+ assert(u);
+
+ /* Frees the set and makes sure we are dropped from the
+ * inverse pointers */
+
+ SET_FOREACH(other, s, i) {
+ UnitDependency d;
+
+ for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+ set_remove(other->dependencies[d], u);
+
+ unit_add_to_gc_queue(other);
+ }
+
+ set_free(s);
+}
+
+void unit_free(Unit *u) {
+ UnitDependency d;
+ Iterator i;
+ char *t;
+
+ assert(u);
+
+ bus_unit_send_removed_signal(u);
+
+ if (u->load_state != UNIT_STUB)
+ if (UNIT_VTABLE(u)->done)
+ UNIT_VTABLE(u)->done(u);
+
+ SET_FOREACH(t, u->names, i)
+ hashmap_remove_value(u->manager->units, t, u);
+
+ if (u->job)
+ job_free(u->job);
+
+ for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+ bidi_set_free(u, u->dependencies[d]);
+
+ if (u->type != _UNIT_TYPE_INVALID)
+ LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
+
+ if (u->in_load_queue)
+ LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
+
+ if (u->in_dbus_queue)
+ LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
+
+ if (u->in_cleanup_queue)
+ LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u);
+
+ if (u->in_gc_queue) {
+ LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u);
+ u->manager->n_in_gc_queue--;
+ }
+
+ cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0);
+ cgroup_attribute_free_list(u->cgroup_attributes);
+
+ free(u->description);
+ free(u->fragment_path);
+ free(u->instance);
+
+ set_free_free(u->names);
+
+ condition_free_list(u->conditions);
+
+ while (u->refs)
+ unit_ref_unset(u->refs);
+
+ free(u);
+}
+
+UnitActiveState unit_active_state(Unit *u) {
+ assert(u);
+
+ if (u->load_state == UNIT_MERGED)
+ return unit_active_state(unit_follow_merge(u));
+
+ /* After a reload it might happen that a unit is not correctly
+ * loaded but still has a process around. That's why we won't
+ * shortcut failed loading to UNIT_INACTIVE_FAILED. */
+
+ return UNIT_VTABLE(u)->active_state(u);
+}
+
+const char* unit_sub_state_to_string(Unit *u) {
+ assert(u);
+
+ return UNIT_VTABLE(u)->sub_state_to_string(u);
+}
+
+static void complete_move(Set **s, Set **other) {
+ assert(s);
+ assert(other);
+
+ if (!*other)
+ return;
+
+ if (*s)
+ set_move(*s, *other);
+ else {
+ *s = *other;
+ *other = NULL;
+ }
+}
+
+static void merge_names(Unit *u, Unit *other) {
+ char *t;
+ Iterator i;
+
+ assert(u);
+ assert(other);
+
+ complete_move(&u->names, &other->names);
+
+ set_free_free(other->names);
+ other->names = NULL;
+ other->id = NULL;
+
+ SET_FOREACH(t, u->names, i)
+ assert_se(hashmap_replace(u->manager->units, t, u) == 0);
+}
+
+static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) {
+ Iterator i;
+ Unit *back;
+ int r;
+
+ assert(u);
+ assert(other);
+ assert(d < _UNIT_DEPENDENCY_MAX);
+
+ /* Fix backwards pointers */
+ SET_FOREACH(back, other->dependencies[d], i) {
+ UnitDependency k;
+
+ for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
+ if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) {
+
+ if (r == -EEXIST)
+ set_remove(back->dependencies[k], other);
+ else
+ assert(r == -ENOENT);
+ }
+ }
+
+ complete_move(&u->dependencies[d], &other->dependencies[d]);
+
+ set_free(other->dependencies[d]);
+ other->dependencies[d] = NULL;
+}
+
+int unit_merge(Unit *u, Unit *other) {
+ UnitDependency d;
+
+ assert(u);
+ assert(other);
+ assert(u->manager == other->manager);
+ assert(u->type != _UNIT_TYPE_INVALID);
+
+ other = unit_follow_merge(other);
+
+ if (other == u)
+ return 0;
+
+ if (u->type != other->type)
+ return -EINVAL;
+
+ if (!u->instance != !other->instance)
+ return -EINVAL;
+
+ if (other->load_state != UNIT_STUB &&
+ other->load_state != UNIT_ERROR)
+ return -EEXIST;
+
+ if (other->job)
+ return -EEXIST;
+
+ if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
+ return -EEXIST;
+
+ /* Merge names */
+ merge_names(u, other);
+
+ /* Redirect all references */
+ while (other->refs)
+ unit_ref_set(other->refs, u);
+
+ /* Merge dependencies */
+ for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+ merge_dependencies(u, other, d);
+
+ other->load_state = UNIT_MERGED;
+ other->merged_into = u;
+
+ /* If there is still some data attached to the other node, we
+ * don't need it anymore, and can free it. */
+ if (other->load_state != UNIT_STUB)
+ if (UNIT_VTABLE(other)->done)
+ UNIT_VTABLE(other)->done(other);
+
+ unit_add_to_dbus_queue(u);
+ unit_add_to_cleanup_queue(other);
+
+ return 0;
+}
+
+int unit_merge_by_name(Unit *u, const char *name) {
+ Unit *other;
+ int r;
+ char *s = NULL;
+
+ assert(u);
+ assert(name);
+
+ if (unit_name_is_template(name)) {
+ if (!u->instance)
+ return -EINVAL;
+
+ if (!(s = unit_name_replace_instance(name, u->instance)))
+ return -ENOMEM;
+
+ name = s;
+ }
+
+ if (!(other = manager_get_unit(u->manager, name)))
+ r = unit_add_name(u, name);
+ else
+ r = unit_merge(u, other);
+
+ free(s);
+ return r;
+}
+
+Unit* unit_follow_merge(Unit *u) {
+ assert(u);
+
+ while (u->load_state == UNIT_MERGED)
+ assert_se(u = u->merged_into);
+
+ return u;
+}
+
+int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
+ int r;
+
+ assert(u);
+ assert(c);
+
+ if (c->std_output != EXEC_OUTPUT_KMSG &&
+ c->std_output != EXEC_OUTPUT_SYSLOG &&
+ c->std_output != EXEC_OUTPUT_JOURNAL &&
+ c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
+ c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE &&
+ c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
+ c->std_error != EXEC_OUTPUT_KMSG &&
+ c->std_error != EXEC_OUTPUT_SYSLOG &&
+ c->std_error != EXEC_OUTPUT_JOURNAL &&
+ c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
+ c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
+ c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE)
+ return 0;
+
+ /* If syslog or kernel logging is requested, make sure our own
+ * logging daemon is run first. */
+
+ if (u->manager->running_as == MANAGER_SYSTEM)
+ if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
+ return r;
+
+ return 0;
+}
+
+const char *unit_description(Unit *u) {
+ assert(u);
+
+ if (u->description)
+ return u->description;
+
+ return strna(u->id);
+}
+
+void unit_dump(Unit *u, FILE *f, const char *prefix) {
+ char *t;
+ UnitDependency d;
+ Iterator i;
+ char *p2;
+ const char *prefix2;
+ char
+ timestamp1[FORMAT_TIMESTAMP_MAX],
+ timestamp2[FORMAT_TIMESTAMP_MAX],
+ timestamp3[FORMAT_TIMESTAMP_MAX],
+ timestamp4[FORMAT_TIMESTAMP_MAX],
+ timespan[FORMAT_TIMESPAN_MAX];
+ Unit *following;
+
+ assert(u);
+ assert(u->type >= 0);
+
+ if (!prefix)
+ prefix = "";
+ p2 = strappend(prefix, "\t");
+ prefix2 = p2 ? p2 : prefix;
+
+ fprintf(f,
+ "%s-> Unit %s:\n"
+ "%s\tDescription: %s\n"
+ "%s\tInstance: %s\n"
+ "%s\tUnit Load State: %s\n"
+ "%s\tUnit Active State: %s\n"
+ "%s\tInactive Exit Timestamp: %s\n"
+ "%s\tActive Enter Timestamp: %s\n"
+ "%s\tActive Exit Timestamp: %s\n"
+ "%s\tInactive Enter Timestamp: %s\n"
+ "%s\tGC Check Good: %s\n"
+ "%s\tNeed Daemon Reload: %s\n",
+ prefix, u->id,
+ prefix, unit_description(u),
+ prefix, strna(u->instance),
+ prefix, unit_load_state_to_string(u->load_state),
+ prefix, unit_active_state_to_string(unit_active_state(u)),
+ prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)),
+ prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
+ prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
+ prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
+ prefix, yes_no(unit_check_gc(u)),
+ prefix, yes_no(unit_need_daemon_reload(u)));
+
+ SET_FOREACH(t, u->names, i)
+ fprintf(f, "%s\tName: %s\n", prefix, t);
+
+ if ((following = unit_following(u)))
+ fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
+
+ if (u->fragment_path)
+ fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
+
+ if (u->job_timeout > 0)
+ fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
+
+ condition_dump_list(u->conditions, f, prefix);
+
+ if (dual_timestamp_is_set(&u->condition_timestamp))
+ fprintf(f,
+ "%s\tCondition Timestamp: %s\n"
+ "%s\tCondition Result: %s\n",
+ prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)),
+ prefix, yes_no(u->condition_result));
+
+ for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
+ Unit *other;
+
+ SET_FOREACH(other, u->dependencies[d], i)
+ fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id);
+ }
+
+ if (u->load_state == UNIT_LOADED) {
+ CGroupBonding *b;
+ CGroupAttribute *a;
+
+ fprintf(f,
+ "%s\tStopWhenUnneeded: %s\n"
+ "%s\tRefuseManualStart: %s\n"
+ "%s\tRefuseManualStop: %s\n"
+ "%s\tDefaultDependencies: %s\n"
+ "%s\tOnFailureIsolate: %s\n"
+ "%s\tIgnoreOnIsolate: %s\n"
+ "%s\tIgnoreOnSnapshot: %s\n",
+ prefix, yes_no(u->stop_when_unneeded),
+ prefix, yes_no(u->refuse_manual_start),
+ prefix, yes_no(u->refuse_manual_stop),
+ prefix, yes_no(u->default_dependencies),
+ prefix, yes_no(u->on_failure_isolate),
+ prefix, yes_no(u->ignore_on_isolate),
+ prefix, yes_no(u->ignore_on_snapshot));
+
+ LIST_FOREACH(by_unit, b, u->cgroup_bondings)
+ fprintf(f, "%s\tControlGroup: %s:%s\n",
+ prefix, b->controller, b->path);
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ char *v = NULL;
+
+ if (a->map_callback)
+ a->map_callback(a->controller, a->name, a->value, &v);
+
+ fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
+ prefix, a->controller, a->name, v ? v : a->value);
+
+ free(v);
+ }
+
+ if (UNIT_VTABLE(u)->dump)
+ UNIT_VTABLE(u)->dump(u, f, prefix2);
+
+ } else if (u->load_state == UNIT_MERGED)
+ fprintf(f,
+ "%s\tMerged into: %s\n",
+ prefix, u->merged_into->id);
+ else if (u->load_state == UNIT_ERROR)
+ fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+
+
+ if (u->job)
+ job_dump(u->job, f, prefix2);
+
+ free(p2);
+}
+
+/* Common implementation for multiple backends */
+int unit_load_fragment_and_dropin(Unit *u) {
+ int r;
+
+ assert(u);
+
+ /* Load a .service file */
+ if ((r = unit_load_fragment(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_STUB)
+ return -ENOENT;
+
+ /* Load drop-in directory data */
+ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
+ return r;
+
+ return 0;
+}
+
+/* Common implementation for multiple backends */
+int unit_load_fragment_and_dropin_optional(Unit *u) {
+ int r;
+
+ assert(u);
+
+ /* Same as unit_load_fragment_and_dropin(), but whether
+ * something can be loaded or not doesn't matter. */
+
+ /* Load a .service file */
+ if ((r = unit_load_fragment(u)) < 0)
+ return r;
+
+ if (u->load_state == UNIT_STUB)
+ u->load_state = UNIT_LOADED;
+
+ /* Load drop-in directory data */
+ if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
+ return r;
+
+ return 0;
+}
+
+int unit_add_default_target_dependency(Unit *u, Unit *target) {
+ assert(u);
+ assert(target);
+
+ if (target->type != UNIT_TARGET)
+ return 0;
+
+ /* Only add the dependency if both units are loaded, so that
+ * that loop check below is reliable */
+ if (u->load_state != UNIT_LOADED ||
+ target->load_state != UNIT_LOADED)
+ return 0;
+
+ /* If either side wants no automatic dependencies, then let's
+ * skip this */
+ if (!u->default_dependencies ||
+ !target->default_dependencies)
+ return 0;
+
+ /* Don't create loops */
+ if (set_get(target->dependencies[UNIT_BEFORE], u))
+ return 0;
+
+ return unit_add_dependency(target, UNIT_AFTER, u, true);
+}
+
+static int unit_add_default_dependencies(Unit *u) {
+ static const UnitDependency deps[] = {
+ UNIT_REQUIRED_BY,
+ UNIT_REQUIRED_BY_OVERRIDABLE,
+ UNIT_WANTED_BY,
+ UNIT_BOUND_BY
+ };
+
+ Unit *target;
+ Iterator i;
+ int r;
+ unsigned k;
+
+ assert(u);
+
+ for (k = 0; k < ELEMENTSOF(deps); k++)
+ SET_FOREACH(target, u->dependencies[deps[k]], i)
+ if ((r = unit_add_default_target_dependency(u, target)) < 0)
+ return r;
+
+ return 0;
+}
+
+int unit_load(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (u->in_load_queue) {
+ LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
+ u->in_load_queue = false;
+ }
+
+ if (u->type == _UNIT_TYPE_INVALID)
+ return -EINVAL;
+
+ if (u->load_state != UNIT_STUB)
+ return 0;
+
+ if (UNIT_VTABLE(u)->load)
+ if ((r = UNIT_VTABLE(u)->load(u)) < 0)
+ goto fail;
+
+ if (u->load_state == UNIT_STUB) {
+ r = -ENOENT;
+ goto fail;
+ }
+
+ if (u->load_state == UNIT_LOADED &&
+ u->default_dependencies)
+ if ((r = unit_add_default_dependencies(u)) < 0)
+ goto fail;
+
+ if (u->on_failure_isolate &&
+ set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
+
+ log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.",
+ u->id);
+
+ r = -EINVAL;
+ goto fail;
+ }
+
+ assert((u->load_state != UNIT_MERGED) == !u->merged_into);
+
+ unit_add_to_dbus_queue(unit_follow_merge(u));
+ unit_add_to_gc_queue(u);
+
+ return 0;
+
+fail:
+ u->load_state = UNIT_ERROR;
+ u->load_error = r;
+ unit_add_to_dbus_queue(u);
+ unit_add_to_gc_queue(u);
+
+ log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r));
+
+ return r;
+}
+
+bool unit_condition_test(Unit *u) {
+ assert(u);
+
+ dual_timestamp_get(&u->condition_timestamp);
+ u->condition_result = condition_test_list(u->conditions);
+
+ return u->condition_result;
+}
+
+/* Errors:
+ * -EBADR: This unit type does not support starting.
+ * -EALREADY: Unit is already started.
+ * -EAGAIN: An operation is already in progress. Retry later.
+ * -ECANCELED: Too many requests for now.
+ */
+int unit_start(Unit *u) {
+ UnitActiveState state;
+ Unit *following;
+
+ assert(u);
+
+ if (u->load_state != UNIT_LOADED)
+ return -EINVAL;
+
+ /* If this is already started, then this will succeed. Note
+ * that this will even succeed if this unit is not startable
+ * by the user. This is relied on to detect when we need to
+ * wait for units and when waiting is finished. */
+ state = unit_active_state(u);
+ if (UNIT_IS_ACTIVE_OR_RELOADING(state))
+ return -EALREADY;
+
+ /* If the conditions failed, don't do anything at all. If we
+ * already are activating this call might still be useful to
+ * speed up activation in case there is some hold-off time,
+ * but we don't want to recheck the condition in that case. */
+ if (state != UNIT_ACTIVATING &&
+ !unit_condition_test(u)) {
+ log_debug("Starting of %s requested but condition failed. Ignoring.", u->id);
+ return -EALREADY;
+ }
+
+ /* Forward to the main object, if we aren't it. */
+ if ((following = unit_following(u))) {
+ log_debug("Redirecting start request from %s to %s.", u->id, following->id);
+ return unit_start(following);
+ }
+
+ /* If it is stopped, but we cannot start it, then fail */
+ if (!UNIT_VTABLE(u)->start)
+ return -EBADR;
+
+ /* We don't suppress calls to ->start() here when we are
+ * already starting, to allow this request to be used as a
+ * "hurry up" call, for example when the unit is in some "auto
+ * restart" state where it waits for a holdoff timer to elapse
+ * before it will start again. */
+
+ unit_add_to_dbus_queue(u);
+
+ unit_status_printf(u, NULL, "Starting %s...", unit_description(u));
+ return UNIT_VTABLE(u)->start(u);
+}
+
+bool unit_can_start(Unit *u) {
+ assert(u);
+
+ return !!UNIT_VTABLE(u)->start;
+}
+
+bool unit_can_isolate(Unit *u) {
+ assert(u);
+
+ return unit_can_start(u) &&
+ u->allow_isolate;
+}
+
+/* Errors:
+ * -EBADR: This unit type does not support stopping.
+ * -EALREADY: Unit is already stopped.
+ * -EAGAIN: An operation is already in progress. Retry later.
+ */
+int unit_stop(Unit *u) {
+ UnitActiveState state;
+ Unit *following;
+
+ assert(u);
+
+ state = unit_active_state(u);
+ if (UNIT_IS_INACTIVE_OR_FAILED(state))
+ return -EALREADY;
+
+ if ((following = unit_following(u))) {
+ log_debug("Redirecting stop request from %s to %s.", u->id, following->id);
+ return unit_stop(following);
+ }
+
+ if (!UNIT_VTABLE(u)->stop)
+ return -EBADR;
+
+ unit_add_to_dbus_queue(u);
+
+ unit_status_printf(u, NULL, "Stopping %s...", unit_description(u));
+ return UNIT_VTABLE(u)->stop(u);
+}
+
+/* Errors:
+ * -EBADR: This unit type does not support reloading.
+ * -ENOEXEC: Unit is not started.
+ * -EAGAIN: An operation is already in progress. Retry later.
+ */
+int unit_reload(Unit *u) {
+ UnitActiveState state;
+ Unit *following;
+
+ assert(u);
+
+ if (u->load_state != UNIT_LOADED)
+ return -EINVAL;
+
+ if (!unit_can_reload(u))
+ return -EBADR;
+
+ state = unit_active_state(u);
+ if (state == UNIT_RELOADING)
+ return -EALREADY;
+
+ if (state != UNIT_ACTIVE)
+ return -ENOEXEC;
+
+ if ((following = unit_following(u))) {
+ log_debug("Redirecting reload request from %s to %s.", u->id, following->id);
+ return unit_reload(following);
+ }
+
+ unit_add_to_dbus_queue(u);
+ return UNIT_VTABLE(u)->reload(u);
+}
+
+bool unit_can_reload(Unit *u) {
+ assert(u);
+
+ if (!UNIT_VTABLE(u)->reload)
+ return false;
+
+ if (!UNIT_VTABLE(u)->can_reload)
+ return true;
+
+ return UNIT_VTABLE(u)->can_reload(u);
+}
+
+static void unit_check_unneeded(Unit *u) {
+ Iterator i;
+ Unit *other;
+
+ assert(u);
+
+ /* If this service shall be shut down when unneeded then do
+ * so. */
+
+ if (!u->stop_when_unneeded)
+ return;
+
+ if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
+ return;
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
+ if (unit_pending_active(other))
+ return;
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
+ if (unit_pending_active(other))
+ return;
+
+ SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
+ if (unit_pending_active(other))
+ return;
+
+ SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
+ if (unit_pending_active(other))
+ return;
+
+ log_info("Service %s is not needed anymore. Stopping.", u->id);
+
+ /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
+ manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+}
+
+static void retroactively_start_dependencies(Unit *u) {
+ Iterator i;
+ Unit *other;
+
+ assert(u);
+ assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
+ if (!set_get(u->dependencies[UNIT_AFTER], other) &&
+ !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
+ if (!set_get(u->dependencies[UNIT_AFTER], other) &&
+ !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
+ if (!set_get(u->dependencies[UNIT_AFTER], other) &&
+ !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
+ if (!set_get(u->dependencies[UNIT_AFTER], other) &&
+ !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
+ if (!set_get(u->dependencies[UNIT_AFTER], other) &&
+ !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+
+ SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+}
+
+static void retroactively_stop_dependencies(Unit *u) {
+ Iterator i;
+ Unit *other;
+
+ assert(u);
+ assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
+
+ /* Pull down units which are bound to us recursively if enabled */
+ SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+}
+
+static void check_unneeded_dependencies(Unit *u) {
+ Iterator i;
+ Unit *other;
+
+ assert(u);
+ assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
+
+ /* Garbage collect services that might not be needed anymore, if enabled */
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+ SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+ SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+ SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+ SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+ SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
+ if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
+ unit_check_unneeded(other);
+}
+
+void unit_trigger_on_failure(Unit *u) {
+ Unit *other;
+ Iterator i;
+
+ assert(u);
+
+ if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
+ return;
+
+ log_info("Triggering OnFailure= dependencies of %s.", u->id);
+
+ SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
+ int r;
+
+ if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0)
+ log_error("Failed to enqueue OnFailure= job: %s", strerror(-r));
+ }
+}
+
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
+ bool unexpected;
+
+ assert(u);
+ assert(os < _UNIT_ACTIVE_STATE_MAX);
+ assert(ns < _UNIT_ACTIVE_STATE_MAX);
+
+ /* Note that this is called for all low-level state changes,
+ * even if they might map to the same high-level
+ * UnitActiveState! That means that ns == os is OK an expected
+ * behaviour here. For example: if a mount point is remounted
+ * this function will be called too! */
+
+ if (u->manager->n_reloading <= 0) {
+ dual_timestamp ts;
+
+ dual_timestamp_get(&ts);
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
+ u->inactive_exit_timestamp = ts;
+ else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns))
+ u->inactive_enter_timestamp = ts;
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
+ u->active_enter_timestamp = ts;
+ else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
+ u->active_exit_timestamp = ts;
+
+ timer_unit_notify(u, ns);
+ path_unit_notify(u, ns);
+ }
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ cgroup_bonding_trim_list(u->cgroup_bondings, true);
+
+ if (u->job) {
+ unexpected = false;
+
+ if (u->job->state == JOB_WAITING)
+
+ /* So we reached a different state for this
+ * job. Let's see if we can run it now if it
+ * failed previously due to EAGAIN. */
+ job_add_to_run_queue(u->job);
+
+ /* Let's check whether this state change constitutes a
+ * finished job, or maybe contradicts a running job and
+ * hence needs to invalidate jobs. */
+
+ switch (u->job->type) {
+
+ case JOB_START:
+ case JOB_VERIFY_ACTIVE:
+
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
+ job_finish_and_invalidate(u->job, JOB_DONE);
+ else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
+ unexpected = true;
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
+ }
+
+ break;
+
+ case JOB_RELOAD:
+ case JOB_RELOAD_OR_START:
+
+ if (u->job->state == JOB_RUNNING) {
+ if (ns == UNIT_ACTIVE)
+ job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED);
+ else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) {
+ unexpected = true;
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
+ }
+ }
+
+ break;
+
+ case JOB_STOP:
+ case JOB_RESTART:
+ case JOB_TRY_RESTART:
+
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ job_finish_and_invalidate(u->job, JOB_DONE);
+ else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
+ unexpected = true;
+ job_finish_and_invalidate(u->job, JOB_FAILED);
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Job type unknown");
+ }
+
+ } else
+ unexpected = true;
+
+ if (u->manager->n_reloading <= 0) {
+
+ /* If this state change happened without being
+ * requested by a job, then let's retroactively start
+ * or stop dependencies. We skip that step when
+ * deserializing, since we don't want to create any
+ * additional jobs just because something is already
+ * activated. */
+
+ if (unexpected) {
+ if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
+ retroactively_start_dependencies(u);
+ else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
+ retroactively_stop_dependencies(u);
+ }
+
+ /* stop unneeded units regardless if going down was expected or not */
+ if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
+ check_unneeded_dependencies(u);
+
+ if (ns != os && ns == UNIT_FAILED) {
+ log_notice("Unit %s entered failed state.", u->id);
+ unit_trigger_on_failure(u);
+ }
+ }
+
+ /* Some names are special */
+ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
+
+ if (unit_has_name(u, SPECIAL_DBUS_SERVICE))
+ /* The bus just might have become available,
+ * hence try to connect to it, if we aren't
+ * yet connected. */
+ bus_init(u->manager, true);
+
+ if (u->type == UNIT_SERVICE &&
+ !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
+ u->manager->n_reloading <= 0) {
+ /* Write audit record if we have just finished starting up */
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+ u->in_audit = true;
+ }
+
+ if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
+ manager_send_unit_plymouth(u->manager, u);
+
+ } else {
+
+ /* We don't care about D-Bus here, since we'll get an
+ * asynchronous notification for it anyway. */
+
+ if (u->type == UNIT_SERVICE &&
+ UNIT_IS_INACTIVE_OR_FAILED(ns) &&
+ !UNIT_IS_INACTIVE_OR_FAILED(os) &&
+ u->manager->n_reloading <= 0) {
+
+ /* Hmm, if there was no start record written
+ * write it now, so that we always have a nice
+ * pair */
+ if (!u->in_audit) {
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
+
+ if (ns == UNIT_INACTIVE)
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+ } else
+ /* Write audit record if we have just finished shutting down */
+ manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+
+ u->in_audit = false;
+ }
+ }
+
+ manager_recheck_journal(u->manager);
+
+ /* Maybe we finished startup and are now ready for being
+ * stopped because unneeded? */
+ unit_check_unneeded(u);
+
+ unit_add_to_dbus_queue(u);
+ unit_add_to_gc_queue(u);
+}
+
+int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
+ struct epoll_event ev;
+
+ assert(u);
+ assert(fd >= 0);
+ assert(w);
+ assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u));
+
+ zero(ev);
+ ev.data.ptr = w;
+ ev.events = events;
+
+ if (epoll_ctl(u->manager->epoll_fd,
+ w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
+ fd,
+ &ev) < 0)
+ return -errno;
+
+ w->fd = fd;
+ w->type = WATCH_FD;
+ w->data.unit = u;
+
+ return 0;
+}
+
+void unit_unwatch_fd(Unit *u, Watch *w) {
+ assert(u);
+ assert(w);
+
+ if (w->type == WATCH_INVALID)
+ return;
+
+ assert(w->type == WATCH_FD);
+ assert(w->data.unit == u);
+ assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+
+ w->fd = -1;
+ w->type = WATCH_INVALID;
+ w->data.unit = NULL;
+}
+
+int unit_watch_pid(Unit *u, pid_t pid) {
+ assert(u);
+ assert(pid >= 1);
+
+ /* Watch a specific PID. We only support one unit watching
+ * each PID for now. */
+
+ return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u);
+}
+
+void unit_unwatch_pid(Unit *u, pid_t pid) {
+ assert(u);
+ assert(pid >= 1);
+
+ hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u);
+}
+
+int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
+ struct itimerspec its;
+ int flags, fd;
+ bool ours;
+
+ assert(u);
+ assert(w);
+ assert(w->type == WATCH_INVALID || (w->type == WATCH_UNIT_TIMER && w->data.unit == u));
+
+ /* This will try to reuse the old timer if there is one */
+
+ if (w->type == WATCH_UNIT_TIMER) {
+ assert(w->data.unit == u);
+ assert(w->fd >= 0);
+
+ ours = false;
+ fd = w->fd;
+ } else if (w->type == WATCH_INVALID) {
+
+ ours = true;
+ if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
+ return -errno;
+ } else
+ assert_not_reached("Invalid watch type");
+
+ zero(its);
+
+ if (delay <= 0) {
+ /* Set absolute time in the past, but not 0, since we
+ * don't want to disarm the timer */
+ its.it_value.tv_sec = 0;
+ its.it_value.tv_nsec = 1;
+
+ flags = TFD_TIMER_ABSTIME;
+ } else {
+ timespec_store(&its.it_value, delay);
+ flags = 0;
+ }
+
+ /* This will also flush the elapse counter */
+ if (timerfd_settime(fd, flags, &its, NULL) < 0)
+ goto fail;
+
+ if (w->type == WATCH_INVALID) {
+ struct epoll_event ev;
+
+ zero(ev);
+ ev.data.ptr = w;
+ ev.events = EPOLLIN;
+
+ if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
+ goto fail;
+ }
+
+ w->type = WATCH_UNIT_TIMER;
+ w->fd = fd;
+ w->data.unit = u;
+
+ return 0;
+
+fail:
+ if (ours)
+ close_nointr_nofail(fd);
+
+ return -errno;
+}
+
+void unit_unwatch_timer(Unit *u, Watch *w) {
+ assert(u);
+ assert(w);
+
+ if (w->type == WATCH_INVALID)
+ return;
+
+ assert(w->type == WATCH_UNIT_TIMER);
+ assert(w->data.unit == u);
+ assert(w->fd >= 0);
+
+ assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+ close_nointr_nofail(w->fd);
+
+ w->fd = -1;
+ w->type = WATCH_INVALID;
+ w->data.unit = NULL;
+}
+
+bool unit_job_is_applicable(Unit *u, JobType j) {
+ assert(u);
+ assert(j >= 0 && j < _JOB_TYPE_MAX);
+
+ switch (j) {
+
+ case JOB_VERIFY_ACTIVE:
+ case JOB_START:
+ case JOB_STOP:
+ return true;
+
+ case JOB_RESTART:
+ case JOB_TRY_RESTART:
+ return unit_can_start(u);
+
+ case JOB_RELOAD:
+ return unit_can_reload(u);
+
+ case JOB_RELOAD_OR_START:
+ return unit_can_reload(u) && unit_can_start(u);
+
+ default:
+ assert_not_reached("Invalid job type");
+ }
+}
+
+int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
+
+ static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
+ [UNIT_REQUIRES] = UNIT_REQUIRED_BY,
+ [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+ [UNIT_WANTS] = UNIT_WANTED_BY,
+ [UNIT_REQUISITE] = UNIT_REQUIRED_BY,
+ [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+ [UNIT_BIND_TO] = UNIT_BOUND_BY,
+ [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_BOUND_BY] = UNIT_BIND_TO,
+ [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
+ [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
+ [UNIT_BEFORE] = UNIT_AFTER,
+ [UNIT_AFTER] = UNIT_BEFORE,
+ [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_REFERENCES] = UNIT_REFERENCED_BY,
+ [UNIT_REFERENCED_BY] = UNIT_REFERENCES,
+ [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY,
+ [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS,
+ [UNIT_PROPAGATE_RELOAD_TO] = UNIT_PROPAGATE_RELOAD_FROM,
+ [UNIT_PROPAGATE_RELOAD_FROM] = UNIT_PROPAGATE_RELOAD_TO
+ };
+ int r, q = 0, v = 0, w = 0;
+
+ assert(u);
+ assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
+ assert(other);
+
+ u = unit_follow_merge(u);
+ other = unit_follow_merge(other);
+
+ /* We won't allow dependencies on ourselves. We will not
+ * consider them an error however. */
+ if (u == other)
+ return 0;
+
+ if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0)
+ return r;
+
+ if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
+ if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
+ return r;
+
+ if (add_reference)
+ if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 ||
+ (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0)
+ return r;
+
+ if ((q = set_put(u->dependencies[d], other)) < 0)
+ return q;
+
+ if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
+ if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) {
+ r = v;
+ goto fail;
+ }
+
+ if (add_reference) {
+ if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) {
+ r = w;
+ goto fail;
+ }
+
+ if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0)
+ goto fail;
+ }
+
+ unit_add_to_dbus_queue(u);
+ return 0;
+
+fail:
+ if (q > 0)
+ set_remove(u->dependencies[d], other);
+
+ if (v > 0)
+ set_remove(other->dependencies[inverse_table[d]], u);
+
+ if (w > 0)
+ set_remove(u->dependencies[UNIT_REFERENCES], other);
+
+ return r;
+}
+
+int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) {
+ int r;
+
+ assert(u);
+
+ if ((r = unit_add_dependency(u, d, other, add_reference)) < 0)
+ return r;
+
+ if ((r = unit_add_dependency(u, e, other, add_reference)) < 0)
+ return r;
+
+ return 0;
+}
+
+static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
+ char *s;
+
+ assert(u);
+ assert(name || path);
+
+ if (!name)
+ name = file_name_from_path(path);
+
+ if (!unit_name_is_template(name)) {
+ *p = NULL;
+ return name;
+ }
+
+ if (u->instance)
+ s = unit_name_replace_instance(name, u->instance);
+ else {
+ char *i;
+
+ if (!(i = unit_name_to_prefix(u->id)))
+ return NULL;
+
+ s = unit_name_replace_instance(name, i);
+ free(i);
+ }
+
+ if (!s)
+ return NULL;
+
+ *p = s;
+ return s;
+}
+
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
+ Unit *other;
+ int r;
+ char *s;
+
+ assert(u);
+ assert(name || path);
+
+ if (!(name = resolve_template(u, name, path, &s)))
+ return -ENOMEM;
+
+ if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
+ goto finish;
+
+ r = unit_add_dependency(u, d, other, add_reference);
+
+finish:
+ free(s);
+ return r;
+}
+
+int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
+ Unit *other;
+ int r;
+ char *s;
+
+ assert(u);
+ assert(name || path);
+
+ if (!(name = resolve_template(u, name, path, &s)))
+ return -ENOMEM;
+
+ if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
+ goto finish;
+
+ r = unit_add_two_dependencies(u, d, e, other, add_reference);
+
+finish:
+ free(s);
+ return r;
+}
+
+int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
+ Unit *other;
+ int r;
+ char *s;
+
+ assert(u);
+ assert(name || path);
+
+ if (!(name = resolve_template(u, name, path, &s)))
+ return -ENOMEM;
+
+ if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
+ goto finish;
+
+ r = unit_add_dependency(other, d, u, add_reference);
+
+finish:
+ free(s);
+ return r;
+}
+
+int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
+ Unit *other;
+ int r;
+ char *s;
+
+ assert(u);
+ assert(name || path);
+
+ if (!(name = resolve_template(u, name, path, &s)))
+ return -ENOMEM;
+
+ if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
+ goto finish;
+
+ if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0)
+ goto finish;
+
+finish:
+ free(s);
+ return r;
+}
+
+int set_unit_path(const char *p) {
+ char *cwd, *c;
+ int r;
+
+ /* This is mostly for debug purposes */
+
+ if (path_is_absolute(p)) {
+ if (!(c = strdup(p)))
+ return -ENOMEM;
+ } else {
+ if (!(cwd = get_current_dir_name()))
+ return -errno;
+
+ r = asprintf(&c, "%s/%s", cwd, p);
+ free(cwd);
+
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
+ r = -errno;
+ free(c);
+ return r;
+ }
+
+ return 0;
+}
+
+char *unit_dbus_path(Unit *u) {
+ char *p, *e;
+
+ assert(u);
+
+ if (!u->id)
+ return NULL;
+
+ if (!(e = bus_path_escape(u->id)))
+ return NULL;
+
+ p = strappend("/org/freedesktop/systemd1/unit/", e);
+ free(e);
+
+ return p;
+}
+
+int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+ int r;
+
+ assert(u);
+ assert(b);
+
+ assert(b->path);
+
+ if (!b->controller) {
+ if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER)))
+ return -ENOMEM;
+
+ b->ours = true;
+ }
+
+ /* Ensure this hasn't been added yet */
+ assert(!b->unit);
+
+ if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ CGroupBonding *l;
+
+ l = hashmap_get(u->manager->cgroup_bondings, b->path);
+ LIST_PREPEND(CGroupBonding, by_path, l, b);
+
+ if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) {
+ LIST_REMOVE(CGroupBonding, by_path, l, b);
+ return r;
+ }
+ }
+
+ LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b);
+ b->unit = u;
+
+ return 0;
+}
+
+static char *default_cgroup_path(Unit *u) {
+ char *p;
+
+ assert(u);
+
+ if (u->instance) {
+ char *t;
+
+ t = unit_name_template(u->id);
+ if (!t)
+ return NULL;
+
+ p = join(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
+ free(t);
+ } else
+ p = join(u->manager->cgroup_hierarchy, "/", u->id, NULL);
+
+ return p;
+}
+
+int unit_add_cgroup_from_text(Unit *u, const char *name) {
+ char *controller = NULL, *path = NULL;
+ CGroupBonding *b = NULL;
+ bool ours = false;
+ int r;
+
+ assert(u);
+ assert(name);
+
+ if ((r = cg_split_spec(name, &controller, &path)) < 0)
+ return r;
+
+ if (!path) {
+ path = default_cgroup_path(u);
+ ours = true;
+ }
+
+ if (!controller) {
+ controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
+ ours = true;
+ }
+
+ if (!path || !controller) {
+ free(path);
+ free(controller);
+
+ return -ENOMEM;
+ }
+
+ if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (!(b = new0(CGroupBonding, 1))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ b->controller = controller;
+ b->path = path;
+ b->ours = ours;
+ b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
+
+ if ((r = unit_add_cgroup(u, b)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ free(path);
+ free(controller);
+ free(b);
+
+ return r;
+}
+
+static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
+ CGroupBonding *b = NULL;
+ int r = -ENOMEM;
+
+ assert(u);
+
+ if (!controller)
+ controller = SYSTEMD_CGROUP_CONTROLLER;
+
+ if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
+ return 0;
+
+ if (!(b = new0(CGroupBonding, 1)))
+ return -ENOMEM;
+
+ if (!(b->controller = strdup(controller)))
+ goto fail;
+
+ if (!(b->path = default_cgroup_path(u)))
+ goto fail;
+
+ b->ours = true;
+ b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
+
+ if ((r = unit_add_cgroup(u, b)) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ free(b->path);
+ free(b->controller);
+ free(b);
+
+ return r;
+}
+
+int unit_add_default_cgroups(Unit *u) {
+ CGroupAttribute *a;
+ char **c;
+ int r;
+
+ assert(u);
+
+ /* Adds in the default cgroups, if they weren't specified
+ * otherwise. */
+
+ if (!u->manager->cgroup_hierarchy)
+ return 0;
+
+ if ((r = unit_add_one_default_cgroup(u, NULL)) < 0)
+ return r;
+
+ STRV_FOREACH(c, u->manager->default_controllers)
+ unit_add_one_default_cgroup(u, *c);
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes)
+ unit_add_one_default_cgroup(u, a->controller);
+
+ return 0;
+}
+
+CGroupBonding* unit_get_default_cgroup(Unit *u) {
+ assert(u);
+
+ return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
+}
+
+int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
+ int r;
+ char *c = NULL;
+ CGroupAttribute *a;
+
+ assert(u);
+ assert(name);
+ assert(value);
+
+ if (!controller) {
+ const char *dot;
+
+ dot = strchr(name, '.');
+ if (!dot)
+ return -EINVAL;
+
+ c = strndup(name, dot - name);
+ if (!c)
+ return -ENOMEM;
+
+ controller = c;
+ }
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ a = new0(CGroupAttribute, 1);
+ if (!a) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (c) {
+ a->controller = c;
+ c = NULL;
+ } else
+ a->controller = strdup(controller);
+
+ a->name = strdup(name);
+ a->value = strdup(value);
+
+ if (!a->controller || !a->name || !a->value) {
+ free(a->controller);
+ free(a->name);
+ free(a->value);
+ free(a);
+
+ return -ENOMEM;
+ }
+
+ a->map_callback = map_callback;
+
+ LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
+
+ r = 0;
+
+finish:
+ free(c);
+ return r;
+}
+
+int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
+ char *t;
+ int r;
+
+ assert(u);
+ assert(type);
+ assert(_found);
+
+ if (!(t = unit_name_change_suffix(u->id, type)))
+ return -ENOMEM;
+
+ assert(!unit_has_name(u, t));
+
+ r = manager_load_unit(u->manager, t, NULL, NULL, _found);
+ free(t);
+
+ assert(r < 0 || *_found != u);
+
+ return r;
+}
+
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+ Unit *found;
+ char *t;
+
+ assert(u);
+ assert(type);
+ assert(_found);
+
+ if (!(t = unit_name_change_suffix(u->id, type)))
+ return -ENOMEM;
+
+ assert(!unit_has_name(u, t));
+
+ found = manager_get_unit(u->manager, t);
+ free(t);
+
+ if (!found)
+ return -ENOENT;
+
+ *_found = found;
+ return 0;
+}
+
+static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return unit_name_to_prefix_and_instance(u->id);
+}
+
+static char *specifier_prefix(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return unit_name_to_prefix(u->id);
+}
+
+static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ char *p, *r;
+
+ assert(u);
+
+ if (!(p = unit_name_to_prefix(u->id)))
+ return NULL;
+
+ r = unit_name_unescape(p);
+ free(p);
+
+ return r;
+}
+
+static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->instance)
+ return unit_name_unescape(u->instance);
+
+ return strdup("");
+}
+
+static char *specifier_filename(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->instance)
+ return unit_name_path_unescape(u->instance);
+
+ return unit_name_to_path(u->instance);
+}
+
+static char *specifier_cgroup(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return default_cgroup_path(u);
+}
+
+static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ char *p;
+ assert(u);
+
+ if (specifier == 'r')
+ return strdup(u->manager->cgroup_hierarchy);
+
+ if (parent_of_path(u->manager->cgroup_hierarchy, &p) < 0)
+ return strdup("");
+
+ if (streq(p, "/")) {
+ free(p);
+ return strdup("");
+ }
+
+ return p;
+}
+
+static char *specifier_runtime(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->manager->running_as == MANAGER_USER) {
+ const char *e;
+
+ e = getenv("XDG_RUNTIME_DIR");
+ if (e)
+ return strdup(e);
+ }
+
+ return strdup("/run");
+}
+
+char *unit_name_printf(Unit *u, const char* format) {
+
+ /*
+ * This will use the passed string as format string and
+ * replace the following specifiers:
+ *
+ * %n: the full id of the unit (foo at bar.waldo)
+ * %N: the id of the unit without the suffix (foo at bar)
+ * %p: the prefix (foo)
+ * %i: the instance (bar)
+ */
+
+ const Specifier table[] = {
+ { 'n', specifier_string, u->id },
+ { 'N', specifier_prefix_and_instance, NULL },
+ { 'p', specifier_prefix, NULL },
+ { 'i', specifier_string, u->instance },
+ { 0, NULL, NULL }
+ };
+
+ assert(u);
+ assert(format);
+
+ return specifier_printf(format, table, u);
+}
+
+char *unit_full_printf(Unit *u, const char *format) {
+
+ /* This is similar to unit_name_printf() but also supports
+ * unescaping. Also, adds a couple of additional codes:
+ *
+ * %c cgroup path of unit
+ * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
+ * %R parent of root cgroup path (e.g. "/usr/lennart/shared")
+ * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
+ */
+
+ const Specifier table[] = {
+ { 'n', specifier_string, u->id },
+ { 'N', specifier_prefix_and_instance, NULL },
+ { 'p', specifier_prefix, NULL },
+ { 'P', specifier_prefix_unescaped, NULL },
+ { 'i', specifier_string, u->instance },
+ { 'I', specifier_instance_unescaped, NULL },
+ { 'f', specifier_filename, NULL },
+ { 'c', specifier_cgroup, NULL },
+ { 'r', specifier_cgroup_root, NULL },
+ { 'R', specifier_cgroup_root, NULL },
+ { 't', specifier_runtime, NULL },
+ { 0, NULL, NULL }
+ };
+
+ assert(u);
+ assert(format);
+
+ return specifier_printf(format, table, u);
+}
+
+char **unit_full_printf_strv(Unit *u, char **l) {
+ size_t n;
+ char **r, **i, **j;
+
+ /* Applies unit_full_printf to every entry in l */
+
+ assert(u);
+
+ n = strv_length(l);
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ for (i = l, j = r; *i; i++, j++)
+ if (!(*j = unit_full_printf(u, *i)))
+ goto fail;
+
+ *j = NULL;
+ return r;
+
+fail:
+ for (j--; j >= r; j--)
+ free(*j);
+
+ free(r);
+
+ return NULL;
+}
+
+int unit_watch_bus_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ /* Watch a specific name on the bus. We only support one unit
+ * watching each name for now. */
+
+ return hashmap_put(u->manager->watch_bus, name, u);
+}
+
+void unit_unwatch_bus_name(Unit *u, const char *name) {
+ assert(u);
+ assert(name);
+
+ hashmap_remove_value(u->manager->watch_bus, name, u);
+}
+
+bool unit_can_serialize(Unit *u) {
+ assert(u);
+
+ return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
+}
+
+int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
+ return r;
+
+ if (u->job)
+ unit_serialize_item(u, f, "job", job_type_to_string(u->job->type));
+
+ dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
+ dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
+ dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
+ dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
+ dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
+
+ if (dual_timestamp_is_set(&u->condition_timestamp))
+ unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
+
+ /* End marker */
+ fputc('\n', f);
+ return 0;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+ va_list ap;
+
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(format);
+
+ fputs(key, f);
+ fputc('=', f);
+
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+
+ fputc('\n', f);
+}
+
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(value);
+
+ fprintf(f, "%s=%s\n", key, value);
+}
+
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
+ int r;
+
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!unit_can_serialize(u))
+ return 0;
+
+ for (;;) {
+ char line[LINE_MAX], *l, *v;
+ size_t k;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ return 0;
+ return -errno;
+ }
+
+ char_array_0(line);
+ l = strstrip(line);
+
+ /* End marker */
+ if (l[0] == 0)
+ return 0;
+
+ k = strcspn(l, "=");
+
+ if (l[k] == '=') {
+ l[k] = 0;
+ v = l+k+1;
+ } else
+ v = l+k;
+
+ if (streq(l, "job")) {
+ JobType type;
+
+ if ((type = job_type_from_string(v)) < 0)
+ log_debug("Failed to parse job type value %s", v);
+ else
+ u->deserialized_job = type;
+
+ continue;
+ } else if (streq(l, "inactive-exit-timestamp")) {
+ dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
+ continue;
+ } else if (streq(l, "active-enter-timestamp")) {
+ dual_timestamp_deserialize(v, &u->active_enter_timestamp);
+ continue;
+ } else if (streq(l, "active-exit-timestamp")) {
+ dual_timestamp_deserialize(v, &u->active_exit_timestamp);
+ continue;
+ } else if (streq(l, "inactive-enter-timestamp")) {
+ dual_timestamp_deserialize(v, &u->inactive_enter_timestamp);
+ continue;
+ } else if (streq(l, "condition-timestamp")) {
+ dual_timestamp_deserialize(v, &u->condition_timestamp);
+ continue;
+ } else if (streq(l, "condition-result")) {
+ int b;
+
+ if ((b = parse_boolean(v)) < 0)
+ log_debug("Failed to parse condition result value %s", v);
+ else
+ u->condition_result = b;
+
+ continue;
+ }
+
+ if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+ return r;
+ }
+}
+
+int unit_add_node_link(Unit *u, const char *what, bool wants) {
+ Unit *device;
+ char *e;
+ int r;
+
+ assert(u);
+
+ if (!what)
+ return 0;
+
+ /* Adds in links to the device node that this unit is based on */
+
+ if (!is_device_path(what))
+ return 0;
+
+ if (!(e = unit_name_build_escape(what+1, NULL, ".device")))
+ return -ENOMEM;
+
+ r = manager_load_unit(u->manager, e, NULL, NULL, &device);
+ free(e);
+
+ if (r < 0)
+ return r;
+
+ if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BIND_TO, device, true)) < 0)
+ return r;
+
+ if (wants)
+ if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0)
+ return r;
+
+ return 0;
+}
+
+int unit_coldplug(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (UNIT_VTABLE(u)->coldplug)
+ if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
+ return r;
+
+ if (u->deserialized_job >= 0) {
+ if ((r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL)) < 0)
+ return r;
+
+ u->deserialized_job = _JOB_TYPE_INVALID;
+ }
+
+ return 0;
+}
+
+void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
+ va_list ap;
+
+ assert(u);
+ assert(format);
+
+ if (!UNIT_VTABLE(u)->show_status)
+ return;
+
+ if (!manager_get_show_status(u->manager))
+ return;
+
+ if (!manager_is_booting_or_shutting_down(u->manager))
+ return;
+
+ va_start(ap, format);
+ status_vprintf(status, true, format, ap);
+ va_end(ap);
+}
+
+bool unit_need_daemon_reload(Unit *u) {
+ assert(u);
+
+ if (u->fragment_path) {
+ struct stat st;
+
+ zero(st);
+ if (stat(u->fragment_path, &st) < 0)
+ /* What, cannot access this anymore? */
+ return true;
+
+ if (u->fragment_mtime > 0 &&
+ timespec_load(&st.st_mtim) != u->fragment_mtime)
+ return true;
+ }
+
+ if (UNIT_VTABLE(u)->need_daemon_reload)
+ return UNIT_VTABLE(u)->need_daemon_reload(u);
+
+ return false;
+}
+
+void unit_reset_failed(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->reset_failed)
+ UNIT_VTABLE(u)->reset_failed(u);
+}
+
+Unit *unit_following(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->following)
+ return UNIT_VTABLE(u)->following(u);
+
+ return NULL;
+}
+
+bool unit_pending_inactive(Unit *u) {
+ assert(u);
+
+ /* Returns true if the unit is inactive or going down */
+
+ if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)))
+ return true;
+
+ if (u->job && u->job->type == JOB_STOP)
+ return true;
+
+ return false;
+}
+
+bool unit_pending_active(Unit *u) {
+ assert(u);
+
+ /* Returns true if the unit is active or going up */
+
+ if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
+ return true;
+
+ if (u->job &&
+ (u->job->type == JOB_START ||
+ u->job->type == JOB_RELOAD_OR_START ||
+ u->job->type == JOB_RESTART))
+ return true;
+
+ return false;
+}
+
+UnitType unit_name_to_type(const char *n) {
+ UnitType t;
+
+ assert(n);
+
+ for (t = 0; t < _UNIT_TYPE_MAX; t++)
+ if (endswith(n, unit_vtable[t]->suffix))
+ return t;
+
+ return _UNIT_TYPE_INVALID;
+}
+
+bool unit_name_is_valid(const char *n, bool template_ok) {
+ UnitType t;
+
+ t = unit_name_to_type(n);
+ if (t < 0 || t >= _UNIT_TYPE_MAX)
+ return false;
+
+ return unit_name_is_valid_no_type(n, template_ok);
+}
+
+int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) {
+ assert(u);
+ assert(w >= 0 && w < _KILL_WHO_MAX);
+ assert(m >= 0 && m < _KILL_MODE_MAX);
+ assert(signo > 0);
+ assert(signo < _NSIG);
+
+ if (m == KILL_NONE)
+ return 0;
+
+ if (!UNIT_VTABLE(u)->kill)
+ return -ENOTSUP;
+
+ return UNIT_VTABLE(u)->kill(u, w, m, signo, error);
+}
+
+int unit_following_set(Unit *u, Set **s) {
+ assert(u);
+ assert(s);
+
+ if (UNIT_VTABLE(u)->following_set)
+ return UNIT_VTABLE(u)->following_set(u, s);
+
+ *s = NULL;
+ return 0;
+}
+
+UnitFileState unit_get_unit_file_state(Unit *u) {
+ assert(u);
+
+ if (u->unit_file_state < 0 && u->fragment_path)
+ u->unit_file_state = unit_file_get_state(
+ u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ NULL, file_name_from_path(u->fragment_path));
+
+ return u->unit_file_state;
+}
+
+Unit* unit_ref_set(UnitRef *ref, Unit *u) {
+ assert(ref);
+ assert(u);
+
+ if (ref->unit)
+ unit_ref_unset(ref);
+
+ ref->unit = u;
+ LIST_PREPEND(UnitRef, refs, u->refs, ref);
+ return u;
+}
+
+void unit_ref_unset(UnitRef *ref) {
+ assert(ref);
+
+ if (!ref->unit)
+ return;
+
+ LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref);
+ ref->unit = NULL;
+}
+
+static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
+ [UNIT_STUB] = "stub",
+ [UNIT_LOADED] = "loaded",
+ [UNIT_ERROR] = "error",
+ [UNIT_MERGED] = "merged",
+ [UNIT_MASKED] = "masked"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
+
+static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
+ [UNIT_ACTIVE] = "active",
+ [UNIT_RELOADING] = "reloading",
+ [UNIT_INACTIVE] = "inactive",
+ [UNIT_FAILED] = "failed",
+ [UNIT_ACTIVATING] = "activating",
+ [UNIT_DEACTIVATING] = "deactivating"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+
+static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
+ [UNIT_REQUIRES] = "Requires",
+ [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
+ [UNIT_WANTS] = "Wants",
+ [UNIT_REQUISITE] = "Requisite",
+ [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
+ [UNIT_REQUIRED_BY] = "RequiredBy",
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_BIND_TO] = "BindTo",
+ [UNIT_WANTED_BY] = "WantedBy",
+ [UNIT_CONFLICTS] = "Conflicts",
+ [UNIT_CONFLICTED_BY] = "ConflictedBy",
+ [UNIT_BOUND_BY] = "BoundBy",
+ [UNIT_BEFORE] = "Before",
+ [UNIT_AFTER] = "After",
+ [UNIT_REFERENCES] = "References",
+ [UNIT_REFERENCED_BY] = "ReferencedBy",
+ [UNIT_ON_FAILURE] = "OnFailure",
+ [UNIT_TRIGGERS] = "Triggers",
+ [UNIT_TRIGGERED_BY] = "TriggeredBy",
+ [UNIT_PROPAGATE_RELOAD_TO] = "PropagateReloadTo",
+ [UNIT_PROPAGATE_RELOAD_FROM] = "PropagateReloadFrom"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
diff --git a/src/core/unit.h b/src/core/unit.h
new file mode 100644
index 0000000..756f465
--- /dev/null
+++ b/src/core/unit.h
@@ -0,0 +1,562 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foounithfoo
+#define foounithfoo
+
+/***
+ 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 General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+typedef struct Unit Unit;
+typedef struct UnitVTable UnitVTable;
+typedef enum UnitType UnitType;
+typedef enum UnitLoadState UnitLoadState;
+typedef enum UnitActiveState UnitActiveState;
+typedef enum UnitDependency UnitDependency;
+typedef struct UnitRef UnitRef;
+
+#include "set.h"
+#include "util.h"
+#include "list.h"
+#include "socket-util.h"
+#include "execute.h"
+#include "condition.h"
+#include "install.h"
+
+enum UnitType {
+ UNIT_SERVICE = 0,
+ UNIT_SOCKET,
+ UNIT_TARGET,
+ UNIT_DEVICE,
+ UNIT_MOUNT,
+ UNIT_AUTOMOUNT,
+ UNIT_SNAPSHOT,
+ UNIT_TIMER,
+ UNIT_SWAP,
+ UNIT_PATH,
+ _UNIT_TYPE_MAX,
+ _UNIT_TYPE_INVALID = -1
+};
+
+enum UnitLoadState {
+ UNIT_STUB,
+ UNIT_LOADED,
+ UNIT_ERROR,
+ UNIT_MERGED,
+ UNIT_MASKED,
+ _UNIT_LOAD_STATE_MAX,
+ _UNIT_LOAD_STATE_INVALID = -1
+};
+
+enum UnitActiveState {
+ UNIT_ACTIVE,
+ UNIT_RELOADING,
+ UNIT_INACTIVE,
+ UNIT_FAILED,
+ UNIT_ACTIVATING,
+ UNIT_DEACTIVATING,
+ _UNIT_ACTIVE_STATE_MAX,
+ _UNIT_ACTIVE_STATE_INVALID = -1
+};
+
+static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
+ return t == UNIT_ACTIVE || t == UNIT_RELOADING;
+}
+
+static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) {
+ return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_RELOADING;
+}
+
+static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) {
+ return t == UNIT_INACTIVE || t == UNIT_FAILED || t == UNIT_DEACTIVATING;
+}
+
+static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
+ return t == UNIT_INACTIVE || t == UNIT_FAILED;
+}
+
+enum UnitDependency {
+ /* Positive dependencies */
+ UNIT_REQUIRES,
+ UNIT_REQUIRES_OVERRIDABLE,
+ UNIT_REQUISITE,
+ UNIT_REQUISITE_OVERRIDABLE,
+ UNIT_WANTS,
+ UNIT_BIND_TO,
+
+ /* Inverse of the above */
+ UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
+ UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
+ UNIT_WANTED_BY, /* inverse of 'wants' */
+ UNIT_BOUND_BY, /* inverse of 'bind_to' */
+
+ /* Negative dependencies */
+ UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
+ UNIT_CONFLICTED_BY,
+
+ /* Order */
+ UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
+ UNIT_AFTER,
+
+ /* On Failure */
+ UNIT_ON_FAILURE,
+
+ /* Triggers (i.e. a socket triggers a service) */
+ UNIT_TRIGGERS,
+ UNIT_TRIGGERED_BY,
+
+ /* Propagate reloads */
+ UNIT_PROPAGATE_RELOAD_TO,
+ UNIT_PROPAGATE_RELOAD_FROM,
+
+ /* Reference information for GC logic */
+ UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
+ UNIT_REFERENCED_BY,
+
+ _UNIT_DEPENDENCY_MAX,
+ _UNIT_DEPENDENCY_INVALID = -1
+};
+
+#include "manager.h"
+#include "job.h"
+#include "cgroup.h"
+#include "cgroup-attr.h"
+
+struct Unit {
+ Manager *manager;
+
+ UnitType type;
+ UnitLoadState load_state;
+ Unit *merged_into;
+
+ char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
+ char *instance;
+
+ Set *names;
+ Set *dependencies[_UNIT_DEPENDENCY_MAX];
+
+ char *description;
+
+ char *fragment_path; /* if loaded from a config file this is the primary path to it */
+ usec_t fragment_mtime;
+
+ /* If there is something to do with this unit, then this is
+ * the job for it */
+ Job *job;
+
+ usec_t job_timeout;
+
+ /* References to this */
+ LIST_HEAD(UnitRef, refs);
+
+ /* Conditions to check */
+ LIST_HEAD(Condition, conditions);
+
+ dual_timestamp condition_timestamp;
+
+ dual_timestamp inactive_exit_timestamp;
+ dual_timestamp active_enter_timestamp;
+ dual_timestamp active_exit_timestamp;
+ dual_timestamp inactive_enter_timestamp;
+
+ /* Counterparts in the cgroup filesystem */
+ CGroupBonding *cgroup_bondings;
+ CGroupAttribute *cgroup_attributes;
+
+ /* Per type list */
+ LIST_FIELDS(Unit, units_by_type);
+
+ /* Load queue */
+ LIST_FIELDS(Unit, load_queue);
+
+ /* D-Bus queue */
+ LIST_FIELDS(Unit, dbus_queue);
+
+ /* Cleanup queue */
+ LIST_FIELDS(Unit, cleanup_queue);
+
+ /* GC queue */
+ LIST_FIELDS(Unit, gc_queue);
+
+ /* Used during GC sweeps */
+ unsigned gc_marker;
+
+ /* When deserializing, temporarily store the job type for this
+ * unit here, if there was a job scheduled */
+ int deserialized_job; /* This is actually of type JobType */
+
+ /* Error code when we didn't manage to load the unit (negative) */
+ int load_error;
+
+ /* Cached unit file state */
+ UnitFileState unit_file_state;
+
+ /* Garbage collect us we nobody wants or requires us anymore */
+ bool stop_when_unneeded;
+
+ /* Create default dependencies */
+ bool default_dependencies;
+
+ /* Refuse manual starting, allow starting only indirectly via dependency. */
+ bool refuse_manual_start;
+
+ /* Don't allow the user to stop this unit manually, allow stopping only indirectly via dependency. */
+ bool refuse_manual_stop;
+
+ /* Allow isolation requests */
+ bool allow_isolate;
+
+ /* Isolate OnFailure unit */
+ bool on_failure_isolate;
+
+ /* Ignore this unit when isolating */
+ bool ignore_on_isolate;
+
+ /* Ignore this unit when snapshotting */
+ bool ignore_on_snapshot;
+
+ /* Did the last condition check suceed? */
+ bool condition_result;
+
+ bool in_load_queue:1;
+ bool in_dbus_queue:1;
+ bool in_cleanup_queue:1;
+ bool in_gc_queue:1;
+
+ bool sent_dbus_new_signal:1;
+
+ bool no_gc:1;
+
+ bool in_audit:1;
+};
+
+struct UnitRef {
+ /* Keeps tracks of references to a unit. This is useful so
+ * that we can merge two units if necessary and correct all
+ * references to them */
+
+ Unit* unit;
+ LIST_FIELDS(UnitRef, refs);
+};
+
+#include "service.h"
+#include "timer.h"
+#include "socket.h"
+#include "target.h"
+#include "device.h"
+#include "mount.h"
+#include "automount.h"
+#include "snapshot.h"
+#include "swap.h"
+#include "path.h"
+
+struct UnitVTable {
+ const char *suffix;
+
+ /* How much memory does an object of this unit type need */
+ size_t object_size;
+
+ /* Config file sections this unit type understands, separated
+ * by NUL chars */
+ const char *sections;
+
+ /* This should reset all type-specific variables. This should
+ * not allocate memory, and is called with zero-initialized
+ * data. It should hence only initialize variables that need
+ * to be set != 0. */
+ void (*init)(Unit *u);
+
+ /* This should free all type-specific variables. It should be
+ * idempotent. */
+ void (*done)(Unit *u);
+
+ /* Actually load data from disk. This may fail, and should set
+ * load_state to UNIT_LOADED, UNIT_MERGED or leave it at
+ * UNIT_STUB if no configuration could be found. */
+ int (*load)(Unit *u);
+
+ /* If a a lot of units got created via enumerate(), this is
+ * where to actually set the state and call unit_notify(). */
+ int (*coldplug)(Unit *u);
+
+ void (*dump)(Unit *u, FILE *f, const char *prefix);
+
+ int (*start)(Unit *u);
+ int (*stop)(Unit *u);
+ int (*reload)(Unit *u);
+
+ int (*kill)(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
+
+ bool (*can_reload)(Unit *u);
+
+ /* Write all data that cannot be restored from other sources
+ * away using unit_serialize_item() */
+ int (*serialize)(Unit *u, FILE *f, FDSet *fds);
+
+ /* Restore one item from the serialization */
+ int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
+
+ /* Boils down the more complex internal state of this unit to
+ * a simpler one that the engine can understand */
+ UnitActiveState (*active_state)(Unit *u);
+
+ /* Returns the substate specific to this unit type as
+ * string. This is purely information so that we can give the
+ * user a more fine grained explanation in which actual state a
+ * unit is in. */
+ const char* (*sub_state_to_string)(Unit *u);
+
+ /* Return true when there is reason to keep this entry around
+ * even nothing references it and it isn't active in any
+ * way */
+ bool (*check_gc)(Unit *u);
+
+ /* Return true when this unit is suitable for snapshotting */
+ bool (*check_snapshot)(Unit *u);
+
+ void (*fd_event)(Unit *u, int fd, uint32_t events, Watch *w);
+ void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
+ void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
+
+ /* Check whether unit needs a daemon reload */
+ bool (*need_daemon_reload)(Unit *u);
+
+ /* Reset failed state if we are in failed state */
+ void (*reset_failed)(Unit *u);
+
+ /* Called whenever any of the cgroups this unit watches for
+ * ran empty */
+ void (*cgroup_notify_empty)(Unit *u);
+
+ /* Called whenever a process of this unit sends us a message */
+ void (*notify_message)(Unit *u, pid_t pid, char **tags);
+
+ /* Called whenever a name thus Unit registered for comes or
+ * goes away. */
+ void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
+
+ /* Called whenever a bus PID lookup finishes */
+ void (*bus_query_pid_done)(Unit *u, const char *name, pid_t pid);
+
+ /* Called for each message received on the bus */
+ DBusHandlerResult (*bus_message_handler)(Unit *u, DBusConnection *c, DBusMessage *message);
+
+ /* Return the unit this unit is following */
+ Unit *(*following)(Unit *u);
+
+ /* Return the set of units that are following each other */
+ int (*following_set)(Unit *u, Set **s);
+
+ /* This is called for each unit type and should be used to
+ * enumerate existing devices and load them. However,
+ * everything that is loaded here should still stay in
+ * inactive state. It is the job of the coldplug() call above
+ * to put the units into the initial state. */
+ int (*enumerate)(Manager *m);
+
+ /* Type specific cleanups. */
+ void (*shutdown)(Manager *m);
+
+ /* When sending out PropertiesChanged signal, which properties
+ * shall be invalidated? This is a NUL separated list of
+ * strings, to minimize relocations a little. */
+ const char *bus_invalidating_properties;
+
+ /* The interface name */
+ const char *bus_interface;
+
+ /* Can units of this type have multiple names? */
+ bool no_alias:1;
+
+ /* Instances make no sense for this type */
+ bool no_instances:1;
+
+ /* Exclude from automatic gc */
+ bool no_gc:1;
+
+ /* Show status updates on the console */
+ bool show_status:1;
+};
+
+extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
+
+#define UNIT_VTABLE(u) unit_vtable[(u)->type]
+
+/* For casting a unit into the various unit types */
+#define DEFINE_CAST(UPPERCASE, MixedCase) \
+ static inline MixedCase* UPPERCASE(Unit *u) { \
+ if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \
+ return NULL; \
+ \
+ return (MixedCase*) u; \
+ }
+
+/* For casting the various unit types into a unit */
+#define UNIT(u) (&(u)->meta)
+
+DEFINE_CAST(SOCKET, Socket);
+DEFINE_CAST(TIMER, Timer);
+DEFINE_CAST(SERVICE, Service);
+DEFINE_CAST(TARGET, Target);
+DEFINE_CAST(DEVICE, Device);
+DEFINE_CAST(MOUNT, Mount);
+DEFINE_CAST(AUTOMOUNT, Automount);
+DEFINE_CAST(SNAPSHOT, Snapshot);
+DEFINE_CAST(SWAP, Swap);
+DEFINE_CAST(PATH, Path);
+
+Unit *unit_new(Manager *m, size_t size);
+void unit_free(Unit *u);
+
+int unit_add_name(Unit *u, const char *name);
+
+int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference);
+int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference);
+
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
+int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
+
+int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
+int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
+
+int unit_add_exec_dependencies(Unit *u, ExecContext *c);
+
+int unit_add_cgroup(Unit *u, CGroupBonding *b);
+int unit_add_cgroup_from_text(Unit *u, const char *name);
+int unit_add_default_cgroups(Unit *u);
+CGroupBonding* unit_get_default_cgroup(Unit *u);
+int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
+
+int unit_choose_id(Unit *u, const char *name);
+int unit_set_description(Unit *u, const char *description);
+
+bool unit_check_gc(Unit *u);
+
+void unit_add_to_load_queue(Unit *u);
+void unit_add_to_dbus_queue(Unit *u);
+void unit_add_to_cleanup_queue(Unit *u);
+void unit_add_to_gc_queue(Unit *u);
+
+int unit_merge(Unit *u, Unit *other);
+int unit_merge_by_name(Unit *u, const char *other);
+
+Unit *unit_follow_merge(Unit *u);
+
+int unit_load_fragment_and_dropin(Unit *u);
+int unit_load_fragment_and_dropin_optional(Unit *u);
+int unit_load(Unit *unit);
+
+const char *unit_description(Unit *u);
+
+bool unit_has_name(Unit *u, const char *name);
+
+UnitActiveState unit_active_state(Unit *u);
+
+const char* unit_sub_state_to_string(Unit *u);
+
+void unit_dump(Unit *u, FILE *f, const char *prefix);
+
+bool unit_can_reload(Unit *u);
+bool unit_can_start(Unit *u);
+bool unit_can_isolate(Unit *u);
+
+int unit_start(Unit *u);
+int unit_stop(Unit *u);
+int unit_reload(Unit *u);
+
+int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
+
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
+
+int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w);
+void unit_unwatch_fd(Unit *u, Watch *w);
+
+int unit_watch_pid(Unit *u, pid_t pid);
+void unit_unwatch_pid(Unit *u, pid_t pid);
+
+int unit_watch_timer(Unit *u, usec_t delay, Watch *w);
+void unit_unwatch_timer(Unit *u, Watch *w);
+
+int unit_watch_bus_name(Unit *u, const char *name);
+void unit_unwatch_bus_name(Unit *u, const char *name);
+
+bool unit_job_is_applicable(Unit *u, JobType j);
+
+int set_unit_path(const char *p);
+
+char *unit_dbus_path(Unit *u);
+
+int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
+int unit_get_related_unit(Unit *u, const char *type, Unit **_found);
+
+char *unit_name_printf(Unit *u, const char* text);
+char *unit_full_printf(Unit *u, const char *text);
+char **unit_full_printf_strv(Unit *u, char **l);
+
+bool unit_can_serialize(Unit *u);
+int unit_serialize(Unit *u, FILE *f, FDSet *fds);
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5);
+void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
+int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
+
+int unit_add_node_link(Unit *u, const char *what, bool wants);
+
+int unit_coldplug(Unit *u);
+
+void unit_status_printf(Unit *u, const char *status, const char *format, ...);
+
+bool unit_need_daemon_reload(Unit *u);
+
+void unit_reset_failed(Unit *u);
+
+Unit *unit_following(Unit *u);
+
+bool unit_pending_inactive(Unit *u);
+bool unit_pending_active(Unit *u);
+
+int unit_add_default_target_dependency(Unit *u, Unit *target);
+
+int unit_following_set(Unit *u, Set **s);
+
+UnitType unit_name_to_type(const char *n);
+bool unit_name_is_valid(const char *n, bool template_ok);
+
+void unit_trigger_on_failure(Unit *u);
+
+bool unit_condition_test(Unit *u);
+
+UnitFileState unit_get_unit_file_state(Unit *u);
+
+Unit* unit_ref_set(UnitRef *ref, Unit *u);
+void unit_ref_unset(UnitRef *ref);
+
+#define UNIT_DEREF(ref) ((ref).unit)
+
+const char *unit_load_state_to_string(UnitLoadState i);
+UnitLoadState unit_load_state_from_string(const char *s);
+
+const char *unit_active_state_to_string(UnitActiveState i);
+UnitActiveState unit_active_state_from_string(const char *s);
+
+const char *unit_dependency_to_string(UnitDependency i);
+UnitDependency unit_dependency_from_string(const char *s);
+
+#endif
diff --git a/src/dbus-automount.c b/src/dbus-automount.c
deleted file mode 100644
index 8e45f81..0000000
--- a/src/dbus-automount.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-automount.h"
-#include "dbus-common.h"
-
-#define BUS_AUTOMOUNT_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Automount\">\n" \
- " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_AUTOMOUNT_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Automount\0"
-
-const char bus_automount_interface[] _introspect_("Automount") = BUS_AUTOMOUNT_INTERFACE;
-
-const char bus_automount_invalidating_properties[] =
- "Result\0";
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_automount_append_automount_result, automount_result, AutomountResult);
-
-static const BusProperty bus_automount_properties[] = {
- { "Where", bus_property_append_string, "s", offsetof(Automount, where), true },
- { "DirectoryMode", bus_property_append_mode, "u", offsetof(Automount, directory_mode) },
- { "Result", bus_automount_append_automount_result, "s", offsetof(Automount, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Automount *am = AUTOMOUNT(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Automount", bus_automount_properties, am },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-automount.h b/src/dbus-automount.h
deleted file mode 100644
index 2fc8345..0000000
--- a/src/dbus-automount.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusautomounthfoo
-#define foodbusautomounthfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_automount_interface[];
-extern const char bus_automount_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-device.c b/src/dbus-device.c
deleted file mode 100644
index b39fb9d..0000000
--- a/src/dbus-device.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "dbus-unit.h"
-#include "dbus-device.h"
-#include "dbus-common.h"
-
-#define BUS_DEVICE_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Device\">\n" \
- " <property name=\"SysFSPath\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_DEVICE_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Device\0"
-
-const char bus_device_interface[] _introspect_("Device") = BUS_DEVICE_INTERFACE;
-
-const char bus_device_invalidating_properties[] =
- "SysFSPath\0";
-
-static const BusProperty bus_device_properties[] = {
- { "SysFSPath", bus_property_append_string, "s", offsetof(Device, sysfs), true },
- { NULL, }
-};
-
-
-DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Device *d = DEVICE(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Device", bus_device_properties, d },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-device.h b/src/dbus-device.h
deleted file mode 100644
index fba270b..0000000
--- a/src/dbus-device.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusdevicehfoo
-#define foodbusdevicehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_device_interface[];
-extern const char bus_device_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-execute.c b/src/dbus-execute.c
deleted file mode 100644
index 1fd2b21..0000000
--- a/src/dbus-execute.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <dbus/dbus.h>
-#include <sys/prctl.h>
-
-#include "dbus-execute.h"
-#include "missing.h"
-#include "ioprio.h"
-#include "strv.h"
-#include "dbus-common.h"
-
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_kill_mode, kill_mode, KillMode);
-
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
-
-int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data) {
- char **env_files = data, **j;
- DBusMessageIter sub, sub2;
-
- assert(i);
- assert(property);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sb)", &sub))
- return -ENOMEM;
-
- STRV_FOREACH(j, env_files) {
- dbus_bool_t b = false;
- char *fn = *j;
-
- if (fn[0] == '-') {
- b = true;
- fn++;
- }
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &fn) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) ||
- !dbus_message_iter_close_container(&sub, &sub2))
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int32_t n;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->oom_score_adjust_set)
- n = c->oom_score_adjust;
- else {
- char *t;
-
- n = 0;
- if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) {
- safe_atoi(t, &n);
- free(t);
- } else if (read_one_line_file("/proc/self/oom_adj", &t) >= 0) {
- safe_atoi(t, &n);
- free(t);
-
- if (n == OOM_ADJUST_MAX)
- n = OOM_SCORE_ADJ_MAX;
- else
- n = (n * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
- }
- }
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int32_t n;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->nice_set)
- n = c->nice;
- else
- n = getpriority(PRIO_PROCESS, 0);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int32_t n;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->ioprio_set)
- n = c->ioprio;
- else
- n = ioprio_get(IOPRIO_WHO_PROCESS, 0);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int32_t n;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->cpu_sched_set)
- n = c->cpu_sched_policy;
- else
- n = sched_getscheduler(0);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int32_t n;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->cpu_sched_set)
- n = c->cpu_sched_priority;
- else {
- struct sched_param p;
- n = 0;
-
- zero(p);
- if (sched_getparam(0, &p) >= 0)
- n = p.sched_priority;
- }
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- dbus_bool_t b;
- DBusMessageIter sub;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub))
- return -ENOMEM;
-
- if (c->cpuset)
- b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus));
- else
- b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &c->cpuset, 0);
-
- if (!b)
- return -ENOMEM;
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- uint64_t u;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->timer_slack_nsec_set)
- u = (uint64_t) c->timer_slack_nsec;
- else
- u = (uint64_t) prctl(PR_GET_TIMERSLACK);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- uint64_t normal, inverted;
-
- assert(i);
- assert(property);
- assert(c);
-
- /* We store this negated internally, to match the kernel, but
- * we expose it normalized. */
-
- normal = *(uint64_t*) data;
- inverted = ~normal;
-
- return bus_property_append_uint64(i, property, &inverted);
-}
-
-int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- char *t = NULL;
- const char *s;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(c);
-
- if (c->capabilities)
- s = t = cap_to_text(c->capabilities, NULL);
- else
- s = "";
-
- if (!s)
- return -ENOMEM;
-
- b = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &s);
-
- if (t)
- cap_free(t);
-
- if (!b)
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data) {
- ExecContext *c = data;
- int r;
- uint64_t u;
-
- assert(i);
- assert(property);
- assert(c);
-
- assert_se((r = rlimit_from_string(property)) >= 0);
-
- if (c->rlimit[r])
- u = (uint64_t) c->rlimit[r]->rlim_max;
- else {
- struct rlimit rl;
-
- zero(rl);
- getrlimit(r, &rl);
-
- u = (uint64_t) rl.rlim_max;
- }
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_execute_append_command(DBusMessageIter *i, const char *property, void *data) {
- ExecCommand *c = data;
- DBusMessageIter sub, sub2, sub3;
-
- assert(i);
- assert(property);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sasbttttuii)", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(command, c, c) {
- char **l;
- uint32_t pid;
- int32_t code, status;
- dbus_bool_t b;
-
- if (!c->path)
- continue;
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &c->path) ||
- !dbus_message_iter_open_container(&sub2, DBUS_TYPE_ARRAY, "s", &sub3))
- return -ENOMEM;
-
- STRV_FOREACH(l, c->argv)
- if (!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, l))
- return -ENOMEM;
-
- pid = (uint32_t) c->exec_status.pid;
- code = (int32_t) c->exec_status.code;
- status = (int32_t) c->exec_status.status;
-
- b = !!c->ignore;
-
- if (!dbus_message_iter_close_container(&sub2, &sub3) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_BOOLEAN, &b) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.realtime) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.start_timestamp.monotonic) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.realtime) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &c->exec_status.exit_timestamp.monotonic) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &code) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_INT32, &status))
- return -ENOMEM;
-
- if (!dbus_message_iter_close_container(&sub, &sub2))
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-const BusProperty bus_exec_context_properties[] = {
- { "Environment", bus_property_append_strv, "as", offsetof(ExecContext, environment), true },
- { "EnvironmentFiles", bus_execute_append_env_files, "a(sb)", offsetof(ExecContext, environment_files), true },
- { "UMask", bus_property_append_mode, "u", offsetof(ExecContext, umask) },
- { "LimitCPU", bus_execute_append_rlimits, "t", 0 },
- { "LimitFSIZE", bus_execute_append_rlimits, "t", 0 },
- { "LimitDATA", bus_execute_append_rlimits, "t", 0 },
- { "LimitSTACK", bus_execute_append_rlimits, "t", 0 },
- { "LimitCORE", bus_execute_append_rlimits, "t", 0 },
- { "LimitRSS", bus_execute_append_rlimits, "t", 0 },
- { "LimitNOFILE", bus_execute_append_rlimits, "t", 0 },
- { "LimitAS", bus_execute_append_rlimits, "t", 0 },
- { "LimitNPROC", bus_execute_append_rlimits, "t", 0 },
- { "LimitMEMLOCK", bus_execute_append_rlimits, "t", 0 },
- { "LimitLOCKS", bus_execute_append_rlimits, "t", 0 },
- { "LimitSIGPENDING", bus_execute_append_rlimits, "t", 0 },
- { "LimitMSGQUEUE", bus_execute_append_rlimits, "t", 0 },
- { "LimitNICE", bus_execute_append_rlimits, "t", 0 },
- { "LimitRTPRIO", bus_execute_append_rlimits, "t", 0 },
- { "LimitRTTIME", bus_execute_append_rlimits, "t", 0 },
- { "WorkingDirectory", bus_property_append_string, "s", offsetof(ExecContext, working_directory), true },
- { "RootDirectory", bus_property_append_string, "s", offsetof(ExecContext, root_directory), true },
- { "OOMScoreAdjust", bus_execute_append_oom_score_adjust, "i", 0 },
- { "Nice", bus_execute_append_nice, "i", 0 },
- { "IOScheduling", bus_execute_append_ioprio, "i", 0 },
- { "CPUSchedulingPolicy", bus_execute_append_cpu_sched_policy, "i", 0 },
- { "CPUSchedulingPriority", bus_execute_append_cpu_sched_priority, "i", 0 },
- { "CPUAffinity", bus_execute_append_affinity, "ay", 0 },
- { "TimerSlackNSec", bus_execute_append_timer_slack_nsec, "t", 0 },
- { "CPUSchedulingResetOnFork", bus_property_append_bool, "b", offsetof(ExecContext, cpu_sched_reset_on_fork) },
- { "NonBlocking", bus_property_append_bool, "b", offsetof(ExecContext, non_blocking) },
- { "StandardInput", bus_execute_append_input, "s", offsetof(ExecContext, std_input) },
- { "StandardOutput", bus_execute_append_output, "s", offsetof(ExecContext, std_output) },
- { "StandardError", bus_execute_append_output, "s", offsetof(ExecContext, std_error) },
- { "TTYPath", bus_property_append_string, "s", offsetof(ExecContext, tty_path), true },
- { "TTYReset", bus_property_append_bool, "b", offsetof(ExecContext, tty_reset) },
- { "TTYVHangup", bus_property_append_bool, "b", offsetof(ExecContext, tty_vhangup) },
- { "TTYVTDisallocate", bus_property_append_bool, "b", offsetof(ExecContext, tty_vt_disallocate) },
- { "SyslogPriority", bus_property_append_int, "i", offsetof(ExecContext, syslog_priority) },
- { "SyslogIdentifier", bus_property_append_string, "s", offsetof(ExecContext, syslog_identifier), true },
- { "SyslogLevelPrefix", bus_property_append_bool, "b", offsetof(ExecContext, syslog_level_prefix) },
- { "Capabilities", bus_execute_append_capabilities, "s", 0 },
- { "SecureBits", bus_property_append_int, "i", offsetof(ExecContext, secure_bits) },
- { "CapabilityBoundingSet", bus_execute_append_capability_bs, "t", offsetof(ExecContext, capability_bounding_set_drop) },
- { "User", bus_property_append_string, "s", offsetof(ExecContext, user), true },
- { "Group", bus_property_append_string, "s", offsetof(ExecContext, group), true },
- { "SupplementaryGroups", bus_property_append_strv, "as", offsetof(ExecContext, supplementary_groups), true },
- { "TCPWrapName", bus_property_append_string, "s", offsetof(ExecContext, tcpwrap_name), true },
- { "PAMName", bus_property_append_string, "s", offsetof(ExecContext, pam_name), true },
- { "ReadWriteDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_write_dirs), true },
- { "ReadOnlyDirectories", bus_property_append_strv, "as", offsetof(ExecContext, read_only_dirs), true },
- { "InaccessibleDirectories", bus_property_append_strv, "as", offsetof(ExecContext, inaccessible_dirs), true },
- { "MountFlags", bus_property_append_ul, "t", offsetof(ExecContext, mount_flags) },
- { "PrivateTmp", bus_property_append_bool, "b", offsetof(ExecContext, private_tmp) },
- { "PrivateNetwork", bus_property_append_bool, "b", offsetof(ExecContext, private_network) },
- { "SameProcessGroup", bus_property_append_bool, "b", offsetof(ExecContext, same_pgrp) },
- { "KillMode", bus_execute_append_kill_mode, "s", offsetof(ExecContext, kill_mode) },
- { "KillSignal", bus_property_append_int, "i", offsetof(ExecContext, kill_signal) },
- { "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true },
- { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) },
- { "ControlGroupPersistent", bus_property_append_tristate_false, "b", offsetof(ExecContext, control_group_persistent) },
- { "IgnoreSIGPIPE", bus_property_append_bool, "b", offsetof(ExecContext, ignore_sigpipe ) },
- { NULL, }
-};
diff --git a/src/dbus-execute.h b/src/dbus-execute.h
deleted file mode 100644
index 03cd69d..0000000
--- a/src/dbus-execute.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusexecutehfoo
-#define foodbusexecutehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "manager.h"
-#include "dbus-common.h"
-
-#define BUS_EXEC_STATUS_INTERFACE(prefix) \
- " <property name=\"" prefix "StartTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"" prefix "StartTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"" prefix "ExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"" prefix "ExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"" prefix "PID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"" prefix "Code\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"" prefix "Status\" type=\"i\" access=\"read\"/>\n"
-
-#define BUS_EXEC_CONTEXT_INTERFACE \
- " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitDATA\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitSTACK\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitCORE\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitRSS\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitNOFILE\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitAS\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitNPROC\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitMEMLOCK\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitLOCKS\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitSIGPENDING\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitMSGQUEUE\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitNICE\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitRTPRIO\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LimitRTTIME\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"WorkingDirectory\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"OOMScoreAdjust\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"Nice\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"IOScheduling\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"CPUSchedulingPolicy\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"CPUSchedulingPriority\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"CPUAffinity\" type=\"ay\" access=\"read\"/>\n" \
- " <property name=\"TimerSlackNS\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"CPUSchedulingResetOnFork\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"NonBlocking\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"StandardInput\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"StandardOutput\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"StandardError\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"TTYPath\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"TTYReset\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"TTYVHangup\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"TTYVTDisallocate\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"SyslogPriority\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"SyslogIdentifier\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"SyslogLevelPrefix\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Capabilities\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"SecureBits\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"CapabilityBoundingSet\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"User\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Group\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"SupplementaryGroups\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"TCPWrapName\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"PAMName\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ReadWriteDirectories\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ReadOnlyDirectories\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n"
-
-#define BUS_EXEC_COMMAND_INTERFACE(name) \
- " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n"
-
-extern const BusProperty bus_exec_context_properties[];
-
-#define BUS_EXEC_COMMAND_PROPERTY(name, command, indirect) \
- { name, bus_execute_append_command, "a(sasbttttuii)", (command), (indirect), NULL }
-
-int bus_execute_append_output(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_input(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_command(DBusMessageIter *u, const char *property, void *data);
-int bus_execute_append_kill_mode(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data);
-
-#endif
diff --git a/src/dbus-job.c b/src/dbus-job.c
deleted file mode 100644
index ab6d610..0000000
--- a/src/dbus-job.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus.h"
-#include "log.h"
-#include "dbus-job.h"
-#include "dbus-common.h"
-
-#define BUS_JOB_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Job\">\n" \
- " <method name=\"Cancel\"/>\n" \
- " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Unit\" type=\"(so)\" access=\"read\"/>\n" \
- " <property name=\"JobType\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_JOB_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-const char bus_job_interface[] _introspect_("Job") = BUS_JOB_INTERFACE;
-
-#define INTERFACES_LIST \
- BUS_GENERIC_INTERFACES_LIST \
- "org.freedesktop.systemd1.Job\0"
-
-#define INVALIDATING_PROPERTIES \
- "State\0"
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
-
-static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Job *j = data;
- DBusMessageIter sub;
- char *p;
-
- assert(i);
- assert(property);
- assert(j);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
- return -ENOMEM;
-
- if (!(p = unit_dbus_path(j->unit)))
- return -ENOMEM;
-
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
- return -ENOMEM;
- }
-
- free(p);
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static const BusProperty bus_job_properties[] = {
- { "Id", bus_property_append_uint32, "u", offsetof(Job, id) },
- { "State", bus_job_append_state, "s", offsetof(Job, state) },
- { "JobType", bus_job_append_type, "s", offsetof(Job, type) },
- { "Unit", bus_job_append_unit, "(so)", 0 },
- { NULL, }
-};
-
-static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
- DBusMessage *reply = NULL;
-
- if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- job_finish_and_invalidate(j, JOB_CANCELED);
-
- } else {
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Job", bus_job_properties, j },
- { NULL, }
- };
- return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
- }
-
- if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- if (reply)
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
- Job *j;
- int r;
- DBusMessage *reply;
-
- assert(connection);
- assert(message);
- assert(m);
-
- if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/job")) {
- /* Be nice to gdbus and return introspection data for our mid-level paths */
-
- if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
- char *introspection = NULL;
- FILE *f;
- Iterator i;
- size_t size;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- /* We roll our own introspection code here, instead of
- * relying on bus_default_message_handler() because we
- * need to generate our introspection string
- * dynamically. */
-
- if (!(f = open_memstream(&introspection, &size)))
- goto oom;
-
- fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
- "<node>\n", f);
-
- fputs(BUS_INTROSPECTABLE_INTERFACE, f);
- fputs(BUS_PEER_INTERFACE, f);
-
- HASHMAP_FOREACH(j, m->jobs, i)
- fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
-
- fputs("</node>\n", f);
-
- if (ferror(f)) {
- fclose(f);
- free(introspection);
- goto oom;
- }
-
- fclose(f);
-
- if (!introspection)
- goto oom;
-
- if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
- free(introspection);
- goto oom;
- }
-
- free(introspection);
-
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
-
- if (r == -ENOMEM)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- if (r == -ENOENT) {
- DBusError e;
-
- dbus_error_init(&e);
- dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
- return bus_send_error_reply(connection, message, &e, r);
- }
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- return bus_job_message_dispatch(j, connection, message);
-
-oom:
- if (reply)
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-const DBusObjectPathVTable bus_job_vtable = {
- .message_function = bus_job_message_handler
-};
-
-static int job_send_message(Job *j, DBusMessage *m) {
- int r;
-
- assert(j);
- assert(m);
-
- if (bus_has_subscriber(j->manager)) {
- if ((r = bus_broadcast(j->manager, m)) < 0)
- return r;
-
- } else if (j->bus_client) {
- /* If nobody is subscribed, we just send the message
- * to the client which created the job */
-
- assert(j->bus);
-
- if (!dbus_message_set_destination(m, j->bus_client))
- return -ENOMEM;
-
- if (!dbus_connection_send(j->bus, m, NULL))
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void bus_job_send_change_signal(Job *j) {
- char *p = NULL;
- DBusMessage *m = NULL;
-
- assert(j);
-
- if (j->in_dbus_queue) {
- LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
- j->in_dbus_queue = false;
- }
-
- if (!bus_has_subscriber(j->manager) && !j->bus_client) {
- j->sent_dbus_new_signal = true;
- return;
- }
-
- if (!(p = job_dbus_path(j)))
- goto oom;
-
- if (j->sent_dbus_new_signal) {
- /* Send a properties changed signal */
-
- if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES)))
- goto oom;
-
- } else {
- /* Send a new signal */
-
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew")))
- goto oom;
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &j->id,
- DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_INVALID))
- goto oom;
- }
-
- if (job_send_message(j, m) < 0)
- goto oom;
-
- free(p);
- dbus_message_unref(m);
-
- j->sent_dbus_new_signal = true;
-
- return;
-
-oom:
- free(p);
-
- if (m)
- dbus_message_unref(m);
-
- log_error("Failed to allocate job change signal.");
-}
-
-void bus_job_send_removed_signal(Job *j) {
- char *p = NULL;
- DBusMessage *m = NULL;
- const char *r;
-
- assert(j);
-
- if (!bus_has_subscriber(j->manager) && !j->bus_client)
- return;
-
- if (!j->sent_dbus_new_signal)
- bus_job_send_change_signal(j);
-
- if (!(p = job_dbus_path(j)))
- goto oom;
-
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved")))
- goto oom;
-
- r = job_result_to_string(j->result);
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &j->id,
- DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_STRING, &r,
- DBUS_TYPE_INVALID))
- goto oom;
-
- if (job_send_message(j, m) < 0)
- goto oom;
-
- free(p);
- dbus_message_unref(m);
-
- return;
-
-oom:
- free(p);
-
- if (m)
- dbus_message_unref(m);
-
- log_error("Failed to allocate job remove signal.");
-}
diff --git a/src/dbus-job.h b/src/dbus-job.h
deleted file mode 100644
index 103c2ff..0000000
--- a/src/dbus-job.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusjobhfoo
-#define foodbusjobhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "job.h"
-
-void bus_job_send_change_signal(Job *j);
-void bus_job_send_removed_signal(Job *j);
-
-extern const DBusObjectPathVTable bus_job_vtable;
-
-extern const char bus_job_interface[];
-
-#endif
diff --git a/src/dbus-loop.h b/src/dbus-loop.h
deleted file mode 100644
index 0bbdfe5..0000000
--- a/src/dbus-loop.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusloophfoo
-#define foodbusloophfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-int bus_loop_open(DBusConnection *c);
-int bus_loop_dispatch(int fd);
-
-#endif
diff --git a/src/dbus-manager.c b/src/dbus-manager.c
deleted file mode 100644
index 3bf0c07..0000000
--- a/src/dbus-manager.c
+++ /dev/null
@@ -1,1544 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <unistd.h>
-
-#include "dbus.h"
-#include "log.h"
-#include "dbus-manager.h"
-#include "strv.h"
-#include "bus-errors.h"
-#include "build.h"
-#include "dbus-common.h"
-#include "install.h"
-
-#define BUS_MANAGER_INTERFACE_BEGIN \
- " <interface name=\"org.freedesktop.systemd1.Manager\">\n"
-
-#define BUS_MANAGER_INTERFACE_METHODS \
- " <method name=\"GetUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"GetUnitByPID\">\n" \
- " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
- " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"LoadUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"StartUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"StartUnitReplace\">\n" \
- " <arg name=\"old_unit\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"new_unit\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"StopUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReloadUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"RestartUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"TryRestartUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReloadOrRestartUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReloadOrTryRestartUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"KillUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"ResetFailedUnit\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"GetJob\">\n" \
- " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ClearJobs\"/>\n" \
- " <method name=\"ResetFailed\"/>\n" \
- " <method name=\"ListUnits\">\n" \
- " <arg name=\"units\" type=\"a(ssssssouso)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ListJobs\">\n" \
- " <arg name=\"jobs\" type=\"a(usssoo)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Subscribe\"/>\n" \
- " <method name=\"Unsubscribe\"/>\n" \
- " <method name=\"Dump\"/>\n" \
- " <method name=\"CreateSnapshot\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Reload\"/>\n" \
- " <method name=\"Reexecute\"/>\n" \
- " <method name=\"Exit\"/>\n" \
- " <method name=\"Reboot\"/>\n" \
- " <method name=\"PowerOff\"/>\n" \
- " <method name=\"Halt\"/>\n" \
- " <method name=\"KExec\"/>\n" \
- " <method name=\"SetEnvironment\">\n" \
- " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"UnsetEnvironment\">\n" \
- " <arg name=\"names\" type=\"as\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"UnsetAndSetEnvironment\">\n" \
- " <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"set\" type=\"as\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"ListUnitFiles\">\n" \
- " <arg name=\"changes\" type=\"a(ss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"GetUnitFileState\">\n" \
- " <arg name=\"file\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"state\" type=\"s\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"EnableUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"DisableUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReenableUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"LinkUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"PresetUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"carries_install_info\" type=\"b\" direction=\"out\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"MaskUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"UnmaskUnitFiles\">\n" \
- " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
- " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
- " </method>\n"
-
-#define BUS_MANAGER_INTERFACE_SIGNALS \
- " <signal name=\"UnitNew\">\n" \
- " <arg name=\"id\" type=\"s\"/>\n" \
- " <arg name=\"unit\" type=\"o\"/>\n" \
- " </signal>\n" \
- " <signal name=\"UnitRemoved\">\n" \
- " <arg name=\"id\" type=\"s\"/>\n" \
- " <arg name=\"unit\" type=\"o\"/>\n" \
- " </signal>\n" \
- " <signal name=\"JobNew\">\n" \
- " <arg name=\"id\" type=\"u\"/>\n" \
- " <arg name=\"job\" type=\"o\"/>\n" \
- " </signal>\n" \
- " <signal name=\"JobRemoved\">\n" \
- " <arg name=\"id\" type=\"u\"/>\n" \
- " <arg name=\"job\" type=\"o\"/>\n" \
- " <arg name=\"result\" type=\"s\"/>\n" \
- " </signal>" \
- " <signal name=\"StartupFinished\">\n" \
- " <arg name=\"kernel\" type=\"t\"/>\n" \
- " <arg name=\"initrd\" type=\"t\"/>\n" \
- " <arg name=\"userspace\" type=\"t\"/>\n" \
- " <arg name=\"total\" type=\"t\"/>\n" \
- " </signal>" \
- " <signal name=\"UnitFilesChanged\"/>\n"
-
-#define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
- " <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Features\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RunningAs\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartupTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \
- " <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
- " <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"NJobs\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"NInstalledJobs\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"NFailedJobs\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Progress\" type=\"d\" access=\"read\"/>\n" \
- " <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"NotifySocket\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"MountAuto\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"SwapAuto\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"read\"/>\n"
-
-#ifdef HAVE_SYSV_COMPAT
-#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
- " <property name=\"SysVConsole\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"SysVInitPath\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"SysVRcndPath\" type=\"as\" access=\"read\"/>\n"
-#else
-#define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV
-#endif
-
-#define BUS_MANAGER_INTERFACE_END \
- " </interface>\n"
-
-#define BUS_MANAGER_INTERFACE \
- BUS_MANAGER_INTERFACE_BEGIN \
- BUS_MANAGER_INTERFACE_METHODS \
- BUS_MANAGER_INTERFACE_SIGNALS \
- BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
- BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \
- BUS_MANAGER_INTERFACE_END
-
-#define INTROSPECTION_BEGIN \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_MANAGER_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE
-
-#define INTROSPECTION_END \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_GENERIC_INTERFACES_LIST \
- "org.freedesktop.systemd1.Manager\0"
-
-const char bus_manager_interface[] _introspect_("Manager") = BUS_MANAGER_INTERFACE;
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_exec_output, exec_output, ExecOutput);
-
-static int bus_manager_append_tainted(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
- Manager *m = data;
- char buf[LINE_MAX] = "", *e = buf, *p = NULL;
-
- assert(i);
- assert(property);
- assert(m);
-
- if (m->taint_usr)
- e = stpcpy(e, "usr-separate-fs ");
-
- if (readlink_malloc("/etc/mtab", &p) < 0)
- e = stpcpy(e, "etc-mtab-not-symlink ");
- else
- free(p);
-
- if (access("/proc/cgroups", F_OK) < 0)
- stpcpy(e, "cgroups-missing ");
-
- t = strstrip(buf);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_manager_append_log_target(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
-
- assert(i);
- assert(property);
-
- t = log_target_to_string(log_get_target());
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_manager_set_log_target(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
-
- assert(i);
- assert(property);
-
- dbus_message_iter_get_basic(i, &t);
-
- return log_set_target_from_string(t);
-}
-
-static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
-
- assert(i);
- assert(property);
-
- t = log_level_to_string(log_get_max_level());
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
-
- assert(i);
- assert(property);
-
- dbus_message_iter_get_basic(i, &t);
-
- return log_set_max_level_from_string(t);
-}
-
-static int bus_manager_append_n_names(DBusMessageIter *i, const char *property, void *data) {
- Manager *m = data;
- uint32_t u;
-
- assert(i);
- assert(property);
- assert(m);
-
- u = hashmap_size(m->units);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_manager_append_n_jobs(DBusMessageIter *i, const char *property, void *data) {
- Manager *m = data;
- uint32_t u;
-
- assert(i);
- assert(property);
- assert(m);
-
- u = hashmap_size(m->jobs);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &u))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_manager_append_progress(DBusMessageIter *i, const char *property, void *data) {
- double d;
- Manager *m = data;
-
- assert(i);
- assert(property);
- assert(m);
-
- if (dual_timestamp_is_set(&m->finish_timestamp))
- d = 1.0;
- else
- d = 1.0 - ((double) hashmap_size(m->jobs) / (double) m->n_installed_jobs);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_DOUBLE, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static const char *message_get_sender_with_fallback(DBusMessage *m) {
- const char *s;
-
- assert(m);
-
- if ((s = dbus_message_get_sender(m)))
- return s;
-
- /* When the message came in from a direct connection the
- * message will have no sender. We fix that here. */
-
- return ":no-sender";
-}
-
-static DBusMessage *message_from_file_changes(
- DBusMessage *m,
- UnitFileChange *changes,
- unsigned n_changes,
- int carries_install_info) {
-
- DBusMessageIter iter, sub, sub2;
- DBusMessage *reply;
- unsigned i;
-
- reply = dbus_message_new_method_return(m);
- if (!reply)
- return NULL;
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (carries_install_info >= 0) {
- dbus_bool_t b;
-
- b = !!carries_install_info;
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
- goto oom;
- }
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
- goto oom;
-
- for (i = 0; i < n_changes; i++) {
- const char *type, *path, *source;
-
- type = unit_file_change_type_to_string(changes[i].type);
- path = strempty(changes[i].path);
- source = strempty(changes[i].source);
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
- !dbus_message_iter_close_container(&sub, &sub2))
- goto oom;
- }
-
- if (!dbus_message_iter_close_container(&iter, &sub))
- goto oom;
-
- return reply;
-
-oom:
- dbus_message_unref(reply);
- return NULL;
-}
-
-static int bus_manager_send_unit_files_changed(Manager *m) {
- DBusMessage *s;
- int r;
-
- s = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
- if (!s)
- return -ENOMEM;
-
- r = bus_broadcast(m, s);
- dbus_message_unref(s);
-
- return r;
-}
-
-static const char systemd_property_string[] =
- PACKAGE_STRING "\0"
- DISTRIBUTION "\0"
- SYSTEMD_FEATURES;
-
-static const BusProperty bus_systemd_properties[] = {
- { "Version", bus_property_append_string, "s", 0 },
- { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) },
- { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) },
- { NULL, }
-};
-
-static const BusProperty bus_manager_properties[] = {
- { "RunningAs", bus_manager_append_running_as, "s", offsetof(Manager, running_as) },
- { "Tainted", bus_manager_append_tainted, "s", 0 },
- { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) },
- { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) },
- { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) },
- { "StartupTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.monotonic) },
- { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
- { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
- { "LogLevel", bus_manager_append_log_level, "s", 0, 0, bus_manager_set_log_level },
- { "LogTarget", bus_manager_append_log_target, "s", 0, 0, bus_manager_set_log_target },
- { "NNames", bus_manager_append_n_names, "u", 0 },
- { "NJobs", bus_manager_append_n_jobs, "u", 0 },
- { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) },
- { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) },
- { "Progress", bus_manager_append_progress, "d", 0 },
- { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true },
- { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
- { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
- { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
- { "NotifySocket", bus_property_append_string, "s", offsetof(Manager, notify_socket), true },
- { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
- { "MountAuto", bus_property_append_bool, "b", offsetof(Manager, mount_auto) },
- { "SwapAuto", bus_property_append_bool, "b", offsetof(Manager, swap_auto) },
- { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
- { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
- { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
- { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), },
- { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), },
-#ifdef HAVE_SYSV_COMPAT
- { "SysVConsole", bus_property_append_bool, "b", offsetof(Manager, sysv_console) },
- { "SysVInitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvinit_path), true },
- { "SysVRcndPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvrcnd_path), true },
-#endif
- { NULL, }
-};
-
-static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
-
- int r;
- DBusError error;
- DBusMessage *reply = NULL;
- char * path = NULL;
- JobType job_type = _JOB_TYPE_INVALID;
- bool reload_if_possible = false;
- const char *member;
-
- assert(connection);
- assert(message);
- assert(m);
-
- dbus_error_init(&error);
-
- member = dbus_message_get_member(message);
-
- if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnit")) {
- const char *name;
- Unit *u;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (!(u = manager_get_unit(m, name))) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = unit_dbus_path(u)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitByPID")) {
- Unit *u;
- uint32_t pid;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_UINT32, &pid,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = unit_dbus_path(u)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LoadUnit")) {
- const char *name;
- Unit *u;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = unit_dbus_path(u)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnit"))
- job_type = JOB_START;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
- job_type = JOB_START;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StopUnit"))
- job_type = JOB_STOP;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadUnit"))
- job_type = JOB_RELOAD;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RestartUnit"))
- job_type = JOB_RESTART;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "TryRestartUnit"))
- job_type = JOB_TRY_RESTART;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrRestartUnit")) {
- reload_if_possible = true;
- job_type = JOB_RESTART;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
- reload_if_possible = true;
- job_type = JOB_TRY_RESTART;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
- const char *name, *swho, *smode;
- int32_t signo;
- Unit *u;
- KillMode mode;
- KillWho who;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &swho,
- DBUS_TYPE_STRING, &smode,
- DBUS_TYPE_INT32, &signo,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (isempty(swho))
- who = KILL_ALL;
- else {
- who = kill_who_from_string(swho);
- if (who < 0)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if (isempty(smode))
- mode = KILL_CONTROL_GROUP;
- else {
- mode = kill_mode_from_string(smode);
- if (mode < 0)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if (signo <= 0 || signo >= _NSIG)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (!(u = manager_get_unit(m, name))) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
- uint32_t id;
- Job *j;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (!(j = manager_get_job(m, id))) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = job_dbus_path(j)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
-
- manager_clear_jobs(m);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
-
- manager_reset_failed(m);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
- const char *name;
- Unit *u;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (!(u = manager_get_unit(m, name))) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- unit_reset_failed(u);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
- DBusMessageIter iter, sub;
- Iterator i;
- Unit *u;
- const char *k;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssssouso)", &sub))
- goto oom;
-
- HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- char *u_path, *j_path;
- const char *description, *load_state, *active_state, *sub_state, *sjob_type, *following;
- DBusMessageIter sub2;
- uint32_t job_id;
- Unit *f;
-
- if (k != u->id)
- continue;
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
- goto oom;
-
- description = unit_description(u);
- load_state = unit_load_state_to_string(u->load_state);
- active_state = unit_active_state_to_string(unit_active_state(u));
- sub_state = unit_sub_state_to_string(u);
-
- f = unit_following(u);
- following = f ? f->id : "";
-
- if (!(u_path = unit_dbus_path(u)))
- goto oom;
-
- if (u->job) {
- job_id = (uint32_t) u->job->id;
-
- if (!(j_path = job_dbus_path(u->job))) {
- free(u_path);
- goto oom;
- }
-
- sjob_type = job_type_to_string(u->job->type);
- } else {
- job_id = 0;
- j_path = u_path;
- sjob_type = "";
- }
-
- if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &u->id) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sub_state) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &following) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &sjob_type) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) {
- free(u_path);
- if (u->job)
- free(j_path);
- goto oom;
- }
-
- free(u_path);
- if (u->job)
- free(j_path);
-
- if (!dbus_message_iter_close_container(&sub, &sub2))
- goto oom;
- }
-
- if (!dbus_message_iter_close_container(&iter, &sub))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListJobs")) {
- DBusMessageIter iter, sub;
- Iterator i;
- Job *j;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(usssoo)", &sub))
- goto oom;
-
- HASHMAP_FOREACH(j, m->jobs, i) {
- char *u_path, *j_path;
- const char *state, *type;
- uint32_t id;
- DBusMessageIter sub2;
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
- goto oom;
-
- id = (uint32_t) j->id;
- state = job_state_to_string(j->state);
- type = job_type_to_string(j->type);
-
- if (!(j_path = job_dbus_path(j)))
- goto oom;
-
- if (!(u_path = unit_dbus_path(j->unit))) {
- free(j_path);
- goto oom;
- }
-
- if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &id) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &j->unit->id) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) {
- free(j_path);
- free(u_path);
- goto oom;
- }
-
- free(j_path);
- free(u_path);
-
- if (!dbus_message_iter_close_container(&sub, &sub2))
- goto oom;
- }
-
- if (!dbus_message_iter_close_container(&iter, &sub))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Subscribe")) {
- char *client;
- Set *s;
-
- if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
- if (!(s = set_new(string_hash_func, string_compare_func)))
- goto oom;
-
- if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
- set_free(s);
- goto oom;
- }
- }
-
- if (!(client = strdup(message_get_sender_with_fallback(message))))
- goto oom;
-
- if ((r = set_put(s, client)) < 0) {
- free(client);
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
- char *client;
-
- if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- free(client);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
- FILE *f;
- char *dump = NULL;
- size_t size;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(f = open_memstream(&dump, &size)))
- goto oom;
-
- manager_dump_units(m, f, NULL);
- manager_dump_jobs(m, f, NULL);
-
- if (ferror(f)) {
- fclose(f);
- free(dump);
- goto oom;
- }
-
- fclose(f);
-
- if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &dump, DBUS_TYPE_INVALID)) {
- free(dump);
- goto oom;
- }
-
- free(dump);
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CreateSnapshot")) {
- const char *name;
- dbus_bool_t cleanup;
- Snapshot *s;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_BOOLEAN, &cleanup,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (name && name[0] == 0)
- name = NULL;
-
- if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = unit_dbus_path(UNIT(s))))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
- char *introspection = NULL;
- FILE *f;
- Iterator i;
- Unit *u;
- Job *j;
- const char *k;
- size_t size;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- /* We roll our own introspection code here, instead of
- * relying on bus_default_message_handler() because we
- * need to generate our introspection string
- * dynamically. */
-
- if (!(f = open_memstream(&introspection, &size)))
- goto oom;
-
- fputs(INTROSPECTION_BEGIN, f);
-
- HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- char *p;
-
- if (k != u->id)
- continue;
-
- if (!(p = bus_path_escape(k))) {
- fclose(f);
- free(introspection);
- goto oom;
- }
-
- fprintf(f, "<node name=\"unit/%s\"/>", p);
- free(p);
- }
-
- HASHMAP_FOREACH(j, m->jobs, i)
- fprintf(f, "<node name=\"job/%lu\"/>", (unsigned long) j->id);
-
- fputs(INTROSPECTION_END, f);
-
- if (ferror(f)) {
- fclose(f);
- free(introspection);
- goto oom;
- }
-
- fclose(f);
-
- if (!introspection)
- goto oom;
-
- if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
- free(introspection);
- goto oom;
- }
-
- free(introspection);
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
-
- assert(!m->queued_message);
-
- /* Instead of sending the reply back right away, we
- * just remember that we need to and then send it
- * after the reload is finished. That way the caller
- * knows when the reload finished. */
-
- if (!(m->queued_message = dbus_message_new_method_return(message)))
- goto oom;
-
- m->queued_message_connection = connection;
- m->exit_code = MANAGER_RELOAD;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
-
- /* We don't send a reply back here, the client should
- * just wait for us disconnecting. */
-
- m->exit_code = MANAGER_REEXECUTE;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
-
- if (m->running_as == MANAGER_SYSTEM) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
- return bus_send_error_reply(connection, message, &error, -ENOTSUP);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- m->exit_code = MANAGER_EXIT;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
-
- if (m->running_as != MANAGER_SYSTEM) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
- return bus_send_error_reply(connection, message, &error, -ENOTSUP);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- m->exit_code = MANAGER_REBOOT;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
-
- if (m->running_as != MANAGER_SYSTEM) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
- return bus_send_error_reply(connection, message, &error, -ENOTSUP);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- m->exit_code = MANAGER_POWEROFF;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
-
- if (m->running_as != MANAGER_SYSTEM) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
- return bus_send_error_reply(connection, message, &error, -ENOTSUP);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- m->exit_code = MANAGER_HALT;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
-
- if (m->running_as != MANAGER_SYSTEM) {
- dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
- return bus_send_error_reply(connection, message, &error, -ENOTSUP);
- }
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- m->exit_code = MANAGER_KEXEC;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
- char **l = NULL, **e = NULL;
-
- if ((r = bus_parse_strv(message, &l)) < 0) {
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- e = strv_env_merge(2, m->environment, l);
- strv_free(l);
-
- if (!e)
- goto oom;
-
- if (!(reply = dbus_message_new_method_return(message))) {
- strv_free(e);
- goto oom;
- }
-
- strv_free(m->environment);
- m->environment = e;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
- char **l = NULL, **e = NULL;
-
- if ((r = bus_parse_strv(message, &l)) < 0) {
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- e = strv_env_delete(m->environment, 1, l);
- strv_free(l);
-
- if (!e)
- goto oom;
-
- if (!(reply = dbus_message_new_method_return(message))) {
- strv_free(e);
- goto oom;
- }
-
- strv_free(m->environment);
- m->environment = e;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
- char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
- DBusMessageIter iter;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_parse_strv_iter(&iter, &l_unset);
- if (r < 0) {
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- if (!dbus_message_iter_next(&iter)) {
- strv_free(l_unset);
- return bus_send_error_reply(connection, message, NULL, -EINVAL);
- }
-
- r = bus_parse_strv_iter(&iter, &l_set);
- if (r < 0) {
- strv_free(l_unset);
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- e = strv_env_delete(m->environment, 1, l_unset);
- strv_free(l_unset);
-
- if (!e) {
- strv_free(l_set);
- goto oom;
- }
-
- f = strv_env_merge(2, e, l_set);
- strv_free(l_set);
- strv_free(e);
-
- if (!f)
- goto oom;
-
- if (!(reply = dbus_message_new_method_return(message))) {
- strv_free(f);
- goto oom;
- }
-
- strv_free(m->environment);
- m->environment = f;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnitFiles")) {
- DBusMessageIter iter, sub, sub2;
- Hashmap *h;
- Iterator i;
- UnitFileList *item;
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- h = hashmap_new(string_hash_func, string_compare_func);
- if (!h)
- goto oom;
-
- r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
- if (r < 0) {
- unit_file_list_free(h);
- dbus_message_unref(reply);
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub)) {
- unit_file_list_free(h);
- goto oom;
- }
-
- HASHMAP_FOREACH(item, h, i) {
- const char *state;
-
- state = unit_file_state_to_string(item->state);
- assert(state);
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &item->path) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) ||
- !dbus_message_iter_close_container(&sub, &sub2)) {
- unit_file_list_free(h);
- goto oom;
- }
- }
-
- unit_file_list_free(h);
-
- if (!dbus_message_iter_close_container(&iter, &sub))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitFileState")) {
- const char *name;
- UnitFileState state;
- const char *s;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- state = unit_file_get_state(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
- if (state < 0)
- return bus_send_error_reply(connection, message, NULL, state);
-
- s = unit_file_state_to_string(state);
- assert(s);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_STRING, &s,
- DBUS_TYPE_INVALID))
- goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "EnableUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
-
- char **l = NULL;
- DBusMessageIter iter;
- UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
- UnitFileChange *changes = NULL;
- unsigned n_changes = 0;
- dbus_bool_t runtime, force;
- int carries_install_info = -1;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_parse_strv_iter(&iter, &l);
- if (r < 0) {
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- if (!dbus_message_iter_next(&iter) ||
- bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0 ||
- bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &force, false) < 0) {
- strv_free(l);
- return bus_send_error_reply(connection, message, NULL, -EIO);
- }
-
- if (streq(member, "EnableUnitFiles")) {
- r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(member, "ReenableUnitFiles")) {
- r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(member, "LinkUnitFiles"))
- r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
- else if (streq(member, "PresetUnitFiles")) {
- r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(member, "MaskUnitFiles"))
- r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
- else
- assert_not_reached("Uh? Wrong method");
-
- strv_free(l);
- bus_manager_send_unit_files_changed(m);
-
- if (r < 0) {
- unit_file_changes_free(changes, n_changes);
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
- unit_file_changes_free(changes, n_changes);
-
- if (!reply)
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "DisableUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnmaskUnitFiles")) {
-
- char **l = NULL;
- DBusMessageIter iter;
- UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
- UnitFileChange *changes = NULL;
- unsigned n_changes = 0;
- dbus_bool_t runtime;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_parse_strv_iter(&iter, &l);
- if (r < 0) {
- if (r == -ENOMEM)
- goto oom;
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- if (!dbus_message_iter_next(&iter) ||
- bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, false) < 0) {
- strv_free(l);
- return bus_send_error_reply(connection, message, NULL, -EIO);
- }
-
- if (streq(member, "DisableUnitFiles"))
- r = unit_file_disable(scope, runtime, NULL, l, &changes, &n_changes);
- else if (streq(member, "UnmaskUnitFiles"))
- r = unit_file_unmask(scope, runtime, NULL, l, &changes, &n_changes);
- else
- assert_not_reached("Uh? Wrong method");
-
- strv_free(l);
- bus_manager_send_unit_files_changed(m);
-
- if (r < 0) {
- unit_file_changes_free(changes, n_changes);
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- reply = message_from_file_changes(message, changes, n_changes, -1);
- unit_file_changes_free(changes, n_changes);
-
- if (!reply)
- goto oom;
-
- } else {
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
- { "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
- { NULL, }
- };
- return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
- }
-
- if (job_type != _JOB_TYPE_INVALID) {
- const char *name, *smode, *old_name = NULL;
- JobMode mode;
- Job *j;
- Unit *u;
- bool b;
-
- if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
- b = dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &old_name,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &smode,
- DBUS_TYPE_INVALID);
- else
- b = dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &smode,
- DBUS_TYPE_INVALID);
-
- if (!b)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (old_name)
- if (!(u = manager_get_unit(m, old_name)) ||
- !u->job ||
- u->job->type != JOB_START) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
-
- if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
- dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (reload_if_possible && unit_can_reload(u)) {
- if (job_type == JOB_RESTART)
- job_type = JOB_RELOAD_OR_START;
- else if (job_type == JOB_TRY_RESTART)
- job_type = JOB_RELOAD;
- }
-
- if ((job_type == JOB_START && u->refuse_manual_start) ||
- (job_type == JOB_STOP && u->refuse_manual_stop) ||
- ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
- (u->refuse_manual_start || u->refuse_manual_stop))) {
- dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
- return bus_send_error_reply(connection, message, &error, -EPERM);
- }
-
- if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(j->bus_client = strdup(message_get_sender_with_fallback(message))))
- goto oom;
-
- j->bus = connection;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = job_dbus_path(j)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
- }
-
- if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
- }
-
- free(path);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- free(path);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-const DBusObjectPathVTable bus_manager_vtable = {
- .message_function = bus_manager_message_handler
-};
diff --git a/src/dbus-manager.h b/src/dbus-manager.h
deleted file mode 100644
index 2eb2fbb..0000000
--- a/src/dbus-manager.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusmanagerhfoo
-#define foodbusmanagerhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-extern const DBusObjectPathVTable bus_manager_vtable;
-
-extern const char bus_manager_interface[];
-
-#endif
diff --git a/src/dbus-mount.c b/src/dbus-mount.c
deleted file mode 100644
index 35d6ea7..0000000
--- a/src/dbus-mount.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-mount.h"
-#include "dbus-execute.h"
-#include "dbus-common.h"
-
-#define BUS_MOUNT_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Mount\">\n" \
- " <property name=\"Where\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Options\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
- BUS_EXEC_COMMAND_INTERFACE("ExecMount") \
- BUS_EXEC_COMMAND_INTERFACE("ExecUnmount") \
- BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \
- BUS_EXEC_CONTEXT_INTERFACE \
- " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_MOUNT_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Mount\0"
-
-const char bus_mount_interface[] _introspect_("Mount") = BUS_MOUNT_INTERFACE;
-
-const char bus_mount_invalidating_properties[] =
- "What\0"
- "Options\0"
- "Type\0"
- "ExecMount\0"
- "ExecUnmount\0"
- "ExecRemount\0"
- "ControlPID\0"
- "Result\0";
-
-static int bus_mount_append_what(DBusMessageIter *i, const char *property, void *data) {
- Mount *m = data;
- const char *d;
-
- assert(i);
- assert(property);
- assert(m);
-
- if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.what)
- d = m->parameters_proc_self_mountinfo.what;
- else if (m->from_fragment && m->parameters_fragment.what)
- d = m->parameters_fragment.what;
- else if (m->from_etc_fstab && m->parameters_etc_fstab.what)
- d = m->parameters_etc_fstab.what;
- else
- d = "";
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_mount_append_options(DBusMessageIter *i, const char *property, void *data) {
- Mount *m = data;
- const char *d;
-
- assert(i);
- assert(property);
- assert(m);
-
- if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.options)
- d = m->parameters_proc_self_mountinfo.options;
- else if (m->from_fragment && m->parameters_fragment.options)
- d = m->parameters_fragment.options;
- else if (m->from_etc_fstab && m->parameters_etc_fstab.options)
- d = m->parameters_etc_fstab.options;
- else
- d = "";
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_mount_append_type(DBusMessageIter *i, const char *property, void *data) {
- Mount *m = data;
- const char *d;
-
- assert(i);
- assert(property);
- assert(m);
-
- if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype)
- d = m->parameters_proc_self_mountinfo.fstype;
- else if (m->from_fragment && m->parameters_fragment.fstype)
- d = m->parameters_fragment.fstype;
- else if (m->from_etc_fstab && m->parameters_etc_fstab.fstype)
- d = m->parameters_etc_fstab.fstype;
- else
- d = "";
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_mount_append_mount_result, mount_result, MountResult);
-
-static const BusProperty bus_mount_properties[] = {
- { "Where", bus_property_append_string, "s", offsetof(Mount, where), true },
- { "What", bus_mount_append_what, "s", 0 },
- { "Options", bus_mount_append_options, "s", 0 },
- { "Type", bus_mount_append_type, "s", 0 },
- { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Mount, timeout_usec) },
- BUS_EXEC_COMMAND_PROPERTY("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), false),
- BUS_EXEC_COMMAND_PROPERTY("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), false),
- BUS_EXEC_COMMAND_PROPERTY("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), false),
- { "ControlPID", bus_property_append_pid, "u", offsetof(Mount, control_pid) },
- { "DirectoryMode", bus_property_append_mode, "u", offsetof(Mount, directory_mode) },
- { "Result", bus_mount_append_mount_result, "s", offsetof(Mount, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Mount *m = MOUNT(u);
-
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
- { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps );
-}
diff --git a/src/dbus-mount.h b/src/dbus-mount.h
deleted file mode 100644
index b5613fa..0000000
--- a/src/dbus-mount.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusmounthfoo
-#define foodbusmounthfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_mount_interface[];
-extern const char bus_mount_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-path.c b/src/dbus-path.c
deleted file mode 100644
index 5506784..0000000
--- a/src/dbus-path.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-path.h"
-#include "dbus-execute.h"
-#include "dbus-common.h"
-
-#define BUS_PATH_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Path\">\n" \
- " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Paths\" type=\"a(ss)\" access=\"read\"/>\n" \
- " <property name=\"MakeDirectory\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_PATH_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Path\0"
-
-const char bus_path_interface[] _introspect_("Path") = BUS_PATH_INTERFACE;
-
-const char bus_path_invalidating_properties[] =
- "Result\0";
-
-static int bus_path_append_paths(DBusMessageIter *i, const char *property, void *data) {
- Path *p = data;
- DBusMessageIter sub, sub2;
- PathSpec *k;
-
- assert(i);
- assert(property);
- assert(p);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(spec, k, p->specs) {
- const char *t = path_type_to_string(k->type);
-
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &k->path) ||
- !dbus_message_iter_close_container(&sub, &sub2))
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Path *p = PATH(u);
- const char *t;
-
- assert(i);
- assert(property);
- assert(u);
-
- t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : "";
-
- return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
-}
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_path_append_path_result, path_result, PathResult);
-
-static const BusProperty bus_path_properties[] = {
- { "Unit", bus_path_append_unit, "s", 0 },
- { "Paths", bus_path_append_paths, "a(ss)", 0 },
- { "MakeDirectory", bus_property_append_bool, "b", offsetof(Path, make_directory) },
- { "DirectoryMode", bus_property_append_mode, "u", offsetof(Path, directory_mode) },
- { "Result", bus_path_append_path_result, "s", offsetof(Path, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Path *p = PATH(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Path", bus_path_properties, p },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-path.h b/src/dbus-path.h
deleted file mode 100644
index 2888400..0000000
--- a/src/dbus-path.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbuspathhfoo
-#define foodbuspathhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_path_interface[];
-
-extern const char bus_path_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-service.c b/src/dbus-service.c
deleted file mode 100644
index d840415..0000000
--- a/src/dbus-service.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-execute.h"
-#include "dbus-service.h"
-#include "dbus-common.h"
-
-#ifdef HAVE_SYSV_COMPAT
-#define BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \
- " <property name=\"SysVStartPriority\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"SysVRunLevels\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"SysVPath\" type=\"s\" access=\"read\"/>\n"
-#else
-#define BUS_SERVICE_SYSV_INTERFACE_FRAGMENT ""
-#endif
-
-#define BUS_SERVICE_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Service\">\n" \
- " <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Restart\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"PIDFile\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"StartLimitAction\" type=\"s\" access=\"readwrite\"/>\n" \
- BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStart") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
- BUS_EXEC_COMMAND_INTERFACE("ExecReload") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStop") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
- BUS_EXEC_CONTEXT_INTERFACE \
- " <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
- BUS_EXEC_STATUS_INTERFACE("ExecMain") \
- " <property name=\"MainPID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"BusName\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"StatusText\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"FsckPassNo\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- BUS_SERVICE_SYSV_INTERFACE_FRAGMENT \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_SERVICE_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Service\0"
-
-const char bus_service_interface[] _introspect_("Service") = BUS_SERVICE_INTERFACE;
-
-const char bus_service_invalidating_properties[] =
- "ExecStartPre\0"
- "ExecStart\0"
- "ExecStartPost\0"
- "ExecReload\0"
- "ExecStop\0"
- "ExecStopPost\0"
- "ExecMain\0"
- "WatchdogTimestamp\0"
- "WatchdogTimestampMonotonic\0"
- "MainPID\0"
- "ControlPID\0"
- "StatusText\0"
- "Result\0";
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_notify_access, notify_access, NotifyAccess);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_service_result, service_result, ServiceResult);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_start_limit_action, start_limit_action, StartLimitAction);
-static DEFINE_BUS_PROPERTY_SET_ENUM(bus_service_set_start_limit_action, start_limit_action, StartLimitAction);
-
-static const BusProperty bus_exec_main_status_properties[] = {
- { "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
- { "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
- { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
- { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
- { "ExecMainPID", bus_property_append_pid, "u", offsetof(ExecStatus, pid) },
- { "ExecMainCode", bus_property_append_int, "i", offsetof(ExecStatus, code) },
- { "ExecMainStatus", bus_property_append_int, "i", offsetof(ExecStatus, status) },
- { NULL, }
-};
-
-static const BusProperty bus_service_properties[] = {
- { "Type", bus_service_append_type, "s", offsetof(Service, type) },
- { "Restart", bus_service_append_restart, "s", offsetof(Service, restart) },
- { "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true },
- { "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) },
- { "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) },
- { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_usec) },
- { "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) },
- { "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) },
- { "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) },
- { "StartLimitInterval", bus_property_append_usec, "t", offsetof(Service, start_limit.interval) },
- { "StartLimitBurst", bus_property_append_uint32, "u", offsetof(Service, start_limit.burst) },
- { "StartLimitAction", bus_service_append_start_limit_action,"s", offsetof(Service, start_limit_action), false, bus_service_set_start_limit_action},
- BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), true ),
- { "PermissionsStartOnly", bus_property_append_bool, "b", offsetof(Service, permissions_start_only) },
- { "RootDirectoryStartOnly", bus_property_append_bool, "b", offsetof(Service, root_directory_start_only) },
- { "RemainAfterExit", bus_property_append_bool, "b", offsetof(Service, remain_after_exit) },
- { "GuessMainPID", bus_property_append_bool, "b", offsetof(Service, guess_main_pid) },
- { "MainPID", bus_property_append_pid, "u", offsetof(Service, main_pid) },
- { "ControlPID", bus_property_append_pid, "u", offsetof(Service, control_pid) },
- { "BusName", bus_property_append_string, "s", offsetof(Service, bus_name), true },
- { "StatusText", bus_property_append_string, "s", offsetof(Service, status_text), true },
-#ifdef HAVE_SYSV_COMPAT
- { "SysVRunLevels", bus_property_append_string, "s", offsetof(Service, sysv_runlevels), true },
- { "SysVStartPriority", bus_property_append_int, "i", offsetof(Service, sysv_start_priority) },
- { "SysVPath", bus_property_append_string, "s", offsetof(Service, sysv_path), true },
-#endif
- { "FsckPassNo", bus_property_append_int, "i", offsetof(Service, fsck_passno) },
- { "Result", bus_service_append_service_result,"s", offsetof(Service, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) {
- Service *s = SERVICE(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Service", bus_service_properties, s },
- { "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context },
- { "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
- { NULL, }
- };
-
- return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-service.h b/src/dbus-service.h
deleted file mode 100644
index d6eab65..0000000
--- a/src/dbus-service.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusservicehfoo
-#define foodbusservicehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_service_interface[];
-extern const char bus_service_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-snapshot.c b/src/dbus-snapshot.c
deleted file mode 100644
index e69388a..0000000
--- a/src/dbus-snapshot.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "dbus-unit.h"
-#include "dbus-snapshot.h"
-#include "dbus-common.h"
-
-#define BUS_SNAPSHOT_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Snapshot\">\n" \
- " <method name=\"Remove\"/>\n" \
- " <property name=\"Cleanup\" type=\"b\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_SNAPSHOT_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Snapshot\0"
-
-const char bus_snapshot_interface[] _introspect_("Snapshot") = BUS_SNAPSHOT_INTERFACE;
-
-static const BusProperty bus_snapshot_properties[] = {
- { "Cleanup", bus_property_append_bool, "b", offsetof(Snapshot, cleanup) },
- { NULL, }
-};
-
-DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Snapshot *s = SNAPSHOT(u);
-
- DBusMessage *reply = NULL;
- DBusError error;
-
- dbus_error_init(&error);
-
- if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) {
-
- snapshot_remove(SNAPSHOT(u));
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else {
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Snapshot", bus_snapshot_properties, s },
- { NULL, }
- };
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
- }
-
- if (reply) {
- if (!dbus_connection_send(c, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
- }
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
diff --git a/src/dbus-snapshot.h b/src/dbus-snapshot.h
deleted file mode 100644
index 0b82279..0000000
--- a/src/dbus-snapshot.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbussnapshothfoo
-#define foodbussnapshothfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_snapshot_interface[];
-
-#endif
diff --git a/src/dbus-socket.c b/src/dbus-socket.c
deleted file mode 100644
index 2e3342c..0000000
--- a/src/dbus-socket.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-socket.h"
-#include "dbus-execute.h"
-#include "dbus-common.h"
-
-#define BUS_SOCKET_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Socket\">\n" \
- " <property name=\"BindIPv6Only\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Backlog\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
- BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStopPre") \
- BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
- BUS_EXEC_CONTEXT_INTERFACE \
- " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"SocketMode\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Accept\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"KeepAlive\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"ReceiveBuffer\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"SendBuffer\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"IPTOS\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"IPTTL\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"PipeSize\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"FreeBind\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Transparent\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Broadcast\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"PassCredentials\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"PassSecurity\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Mark\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"MaxConnections\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"NAccepted\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \
- " <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n" \
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_SOCKET_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Socket\0"
-
-const char bus_socket_interface[] _introspect_("Socket") = BUS_SOCKET_INTERFACE;
-
-const char bus_socket_invalidating_properties[] =
- "ExecStartPre\0"
- "ExecStartPost\0"
- "ExecStopPre\0"
- "ExecStopPost\0"
- "ControlPID\0"
- "NAccepted\0"
- "NConnections\0"
- "Result\0";
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult);
-
-static const BusProperty bus_socket_properties[] = {
- { "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) },
- { "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) },
- { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Socket, timeout_usec) },
- BUS_EXEC_COMMAND_PROPERTY("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), true ),
- BUS_EXEC_COMMAND_PROPERTY("ExecStopPost", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_POST]), true ),
- { "ControlPID", bus_property_append_pid, "u", offsetof(Socket, control_pid) },
- { "BindToDevice", bus_property_append_string, "s", offsetof(Socket, bind_to_device), true },
- { "DirectoryMode", bus_property_append_mode, "u", offsetof(Socket, directory_mode) },
- { "SocketMode", bus_property_append_mode, "u", offsetof(Socket, socket_mode) },
- { "Accept", bus_property_append_bool, "b", offsetof(Socket, accept) },
- { "KeepAlive", bus_property_append_bool, "b", offsetof(Socket, keep_alive) },
- { "Priority", bus_property_append_int, "i", offsetof(Socket, priority) },
- { "ReceiveBuffer", bus_property_append_size, "t", offsetof(Socket, receive_buffer) },
- { "SendBuffer", bus_property_append_size, "t", offsetof(Socket, send_buffer) },
- { "IPTOS", bus_property_append_int, "i", offsetof(Socket, ip_tos) },
- { "IPTTL", bus_property_append_int, "i", offsetof(Socket, ip_ttl) },
- { "PipeSize", bus_property_append_size, "t", offsetof(Socket, pipe_size) },
- { "FreeBind", bus_property_append_bool, "b", offsetof(Socket, free_bind) },
- { "Transparent", bus_property_append_bool, "b", offsetof(Socket, transparent) },
- { "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) },
- { "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) },
- { "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) },
- { "Mark", bus_property_append_int, "i", offsetof(Socket, mark) },
- { "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) },
- { "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) },
- { "NAccepted", bus_property_append_unsigned, "u", offsetof(Socket, n_accepted) },
- { "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) },
- { "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) },
- { "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Socket *s = SOCKET(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
- { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-socket.h b/src/dbus-socket.h
deleted file mode 100644
index 069a2f5..0000000
--- a/src/dbus-socket.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbussockethfoo
-#define foodbussockethfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_socket_interface[];
-extern const char bus_socket_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-swap.c b/src/dbus-swap.c
deleted file mode 100644
index 09cd1e8..0000000
--- a/src/dbus-swap.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2010 Maarten Lankhorst
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-swap.h"
-#include "dbus-execute.h"
-#include "dbus-common.h"
-
-#define BUS_SWAP_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Swap\">\n" \
- " <property name=\"What\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
- BUS_EXEC_COMMAND_INTERFACE("ExecActivate") \
- BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \
- BUS_EXEC_CONTEXT_INTERFACE \
- " <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_SWAP_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Swap\0"
-
-const char bus_swap_interface[] _introspect_("Swap") = BUS_SWAP_INTERFACE;
-
-const char bus_swap_invalidating_properties[] =
- "What\0"
- "Priority\0"
- "ExecActivate\0"
- "ExecDeactivate\0"
- "ControlPID\0"
- "Result\0";
-
-static int bus_swap_append_priority(DBusMessageIter *i, const char *property, void *data) {
- Swap *s = data;
- dbus_int32_t j;
-
- assert(i);
- assert(property);
- assert(s);
-
- if (s->from_proc_swaps)
- j = s->parameters_proc_swaps.priority;
- else if (s->from_fragment)
- j = s->parameters_fragment.priority;
- else if (s->from_etc_fstab)
- j = s->parameters_etc_fstab.priority;
- else
- j = -1;
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &j))
- return -ENOMEM;
-
- return 0;
-}
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_swap_append_swap_result, swap_result, SwapResult);
-
-static const BusProperty bus_swap_properties[] = {
- { "What", bus_property_append_string, "s", offsetof(Swap, what), true },
- { "Priority", bus_swap_append_priority, "i", 0 },
- BUS_EXEC_COMMAND_PROPERTY("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), false),
- BUS_EXEC_COMMAND_PROPERTY("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), false),
- { "ControlPID", bus_property_append_pid, "u", offsetof(Swap, control_pid) },
- { "Result", bus_swap_append_swap_result,"s", offsetof(Swap, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Swap *s = SWAP(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
- { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-swap.h b/src/dbus-swap.h
deleted file mode 100644
index 15b9147..0000000
--- a/src/dbus-swap.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusswaphfoo
-#define foodbusswaphfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2010 Maarten Lankhorst
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_swap_interface[];
-extern const char bus_swap_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-target.c b/src/dbus-target.c
deleted file mode 100644
index 55cf862..0000000
--- a/src/dbus-target.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-target.h"
-#include "dbus-common.h"
-
-#define BUS_TARGET_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Target\">\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_TARGET_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Target\0"
-
-const char bus_target_interface[] _introspect_("Target") = BUS_TARGET_INTERFACE;
-
-DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-target.h b/src/dbus-target.h
deleted file mode 100644
index 13d3876..0000000
--- a/src/dbus-target.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbustargethfoo
-#define foodbustargethfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_target_interface[];
-
-#endif
diff --git a/src/dbus-timer.c b/src/dbus-timer.c
deleted file mode 100644
index b396aed..0000000
--- a/src/dbus-timer.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus-unit.h"
-#include "dbus-timer.h"
-#include "dbus-execute.h"
-#include "dbus-common.h"
-
-#define BUS_TIMER_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Timer\">\n" \
- " <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \
- " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define INTROSPECTION \
- DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
- "<node>\n" \
- BUS_UNIT_INTERFACE \
- BUS_TIMER_INTERFACE \
- BUS_PROPERTIES_INTERFACE \
- BUS_PEER_INTERFACE \
- BUS_INTROSPECTABLE_INTERFACE \
- "</node>\n"
-
-#define INTERFACES_LIST \
- BUS_UNIT_INTERFACES_LIST \
- "org.freedesktop.systemd1.Timer\0"
-
-const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE;
-
-const char bus_timer_invalidating_properties[] =
- "Timers\0"
- "NextElapseUSec\0"
- "Result\0";
-
-static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) {
- Timer *p = data;
- DBusMessageIter sub, sub2;
- TimerValue *k;
-
- assert(i);
- assert(property);
- assert(p);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(stt)", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(value, k, p->values) {
- char *buf;
- const char *t;
- size_t l;
- bool b;
-
- t = timer_base_to_string(k->base);
- assert(endswith(t, "Sec"));
-
- /* s/Sec/USec/ */
- l = strlen(t);
- if (!(buf = new(char, l+2)))
- return -ENOMEM;
-
- memcpy(buf, t, l-3);
- memcpy(buf+l-3, "USec", 5);
-
- b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->value) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
- dbus_message_iter_close_container(&sub, &sub2);
-
- free(buf);
- if (!b)
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Timer *timer = TIMER(u);
- const char *t;
-
- assert(i);
- assert(property);
- assert(u);
-
- t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : "";
-
- return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
-}
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult);
-
-static const BusProperty bus_timer_properties[] = {
- { "Unit", bus_timer_append_unit, "s", 0 },
- { "Timers", bus_timer_append_timers, "a(stt)", 0 },
- { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse) },
- { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) },
- { NULL, }
-};
-
-DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
- Timer *t = TIMER(u);
- const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Timer", bus_timer_properties, t },
- { NULL, }
- };
-
- return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
-}
diff --git a/src/dbus-timer.h b/src/dbus-timer.h
deleted file mode 100644
index e692e12..0000000
--- a/src/dbus-timer.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbustimerhfoo
-#define foodbustimerhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "unit.h"
-
-DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
-
-extern const char bus_timer_interface[];
-extern const char bus_timer_invalidating_properties[];
-
-#endif
diff --git a/src/dbus-unit.c b/src/dbus-unit.c
deleted file mode 100644
index c7532c7..0000000
--- a/src/dbus-unit.c
+++ /dev/null
@@ -1,850 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "dbus.h"
-#include "log.h"
-#include "dbus-unit.h"
-#include "bus-errors.h"
-#include "dbus-common.h"
-
-const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
-
-#define INVALIDATING_PROPERTIES \
- "LoadState\0" \
- "ActiveState\0" \
- "SubState\0" \
- "InactiveExitTimestamp\0" \
- "ActiveEnterTimestamp\0" \
- "ActiveExitTimestamp\0" \
- "InactiveEnterTimestamp\0" \
- "Job\0" \
- "NeedDaemonReload\0"
-
-static int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data) {
- char *t;
- Iterator j;
- DBusMessageIter sub;
- Unit *u = data;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
- return -ENOMEM;
-
- SET_FOREACH(t, u->names, j)
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t))
- return -ENOMEM;
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data, *f;
- const char *d;
-
- assert(i);
- assert(property);
- assert(u);
-
- f = unit_following(u);
- d = f ? f->id : "";
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
- Unit *u;
- Iterator j;
- DBusMessageIter sub;
- Set *s = data;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
- return -ENOMEM;
-
- SET_FOREACH(u, s, j)
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &u->id))
- return -ENOMEM;
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_description(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- const char *d;
-
- assert(i);
- assert(property);
- assert(u);
-
- d = unit_description(u);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
- return -ENOMEM;
-
- return 0;
-}
-
-static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_unit_append_load_state, unit_load_state, UnitLoadState);
-
-static int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- const char *state;
-
- assert(i);
- assert(property);
- assert(u);
-
- state = unit_active_state_to_string(unit_active_state(u));
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- const char *state;
-
- assert(i);
- assert(property);
- assert(u);
-
- state = unit_sub_state_to_string(u);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- const char *state;
-
- assert(i);
- assert(property);
- assert(u);
-
- state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(u);
-
- b = unit_can_start(u) &&
- !u->refuse_manual_start;
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(u);
-
- /* On the lower levels we assume that every unit we can start
- * we can also stop */
-
- b = unit_can_start(u) &&
- !u->refuse_manual_stop;
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(u);
-
- b = unit_can_reload(u);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(u);
-
- b = unit_can_isolate(u) &&
- !u->refuse_manual_start;
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- DBusMessageIter sub;
- char *p;
-
- assert(i);
- assert(property);
- assert(u);
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
- return -ENOMEM;
-
- if (u->job) {
-
- if (!(p = job_dbus_path(u->job)))
- return -ENOMEM;
-
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
- return -ENOMEM;
- }
- } else {
- uint32_t id = 0;
-
- /* No job, so let's fill in some placeholder
- * data. Since we need to fill in a valid path we
- * simple point to ourselves. */
-
- if (!(p = unit_dbus_path(u)))
- return -ENOMEM;
-
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
- return -ENOMEM;
- }
- }
-
- free(p);
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- char *t;
- CGroupBonding *cgb;
- bool success;
-
- assert(i);
- assert(property);
- assert(u);
-
- if ((cgb = unit_get_default_cgroup(u))) {
- if (!(t = cgroup_bonding_to_string(cgb)))
- return -ENOMEM;
- } else
- t = (char*) "";
-
- success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
-
- if (cgb)
- free(t);
-
- return success ? 0 : -ENOMEM;
-}
-
-static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- CGroupBonding *cgb;
- DBusMessageIter sub;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
- char *t;
- bool success;
-
- if (!(t = cgroup_bonding_to_string(cgb)))
- return -ENOMEM;
-
- success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
- free(t);
-
- if (!success)
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- CGroupAttribute *a;
- DBusMessageIter sub, sub2;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
- bool success;
-
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
-
- success =
- dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
- dbus_message_iter_close_container(&sub, &sub2);
-
- free(v);
-
- if (!success)
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
-
- assert(i);
- assert(property);
- assert(u);
-
- b = unit_need_daemon_reload(u);
-
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
- return -ENOMEM;
-
- return 0;
-}
-
-static int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- const char *name, *message;
- DBusMessageIter sub;
-
- assert(i);
- assert(property);
- assert(u);
-
- if (u->load_error != 0) {
- name = bus_errno_to_dbus(u->load_error);
- message = strempty(strerror(-u->load_error));
- } else
- name = message = "";
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
- !dbus_message_iter_close_container(i, &sub))
- return -ENOMEM;
-
- return 0;
-}
-
-static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
- DBusMessage *reply = NULL;
- Manager *m = u->manager;
- DBusError error;
- JobType job_type = _JOB_TYPE_INVALID;
- char *path = NULL;
- bool reload_if_possible = false;
-
- dbus_error_init(&error);
-
- if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Start"))
- job_type = JOB_START;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Stop"))
- job_type = JOB_STOP;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Reload"))
- job_type = JOB_RELOAD;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Restart"))
- job_type = JOB_RESTART;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "TryRestart"))
- job_type = JOB_TRY_RESTART;
- else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrRestart")) {
- reload_if_possible = true;
- job_type = JOB_RESTART;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
- reload_if_possible = true;
- job_type = JOB_TRY_RESTART;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
- const char *swho, *smode;
- int32_t signo;
- KillMode mode;
- KillWho who;
- int r;
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &swho,
- DBUS_TYPE_STRING, &smode,
- DBUS_TYPE_INT32, &signo,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (isempty(swho))
- who = KILL_ALL;
- else {
- who = kill_who_from_string(swho);
- if (who < 0)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if (isempty(smode))
- mode = KILL_CONTROL_GROUP;
- else {
- mode = kill_mode_from_string(smode);
- if (mode < 0)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if (signo <= 0 || signo >= _NSIG)
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
-
- unit_reset_failed(u);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- } else if (UNIT_VTABLE(u)->bus_message_handler)
- return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
- else
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- if (job_type != _JOB_TYPE_INVALID) {
- const char *smode;
- JobMode mode;
- Job *j;
- int r;
-
- if ((job_type == JOB_START && u->refuse_manual_start) ||
- (job_type == JOB_STOP && u->refuse_manual_stop) ||
- ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
- (u->refuse_manual_start || u->refuse_manual_stop))) {
- dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
- return bus_send_error_reply(connection, message, &error, -EPERM);
- }
-
- if (!dbus_message_get_args(
- message,
- &error,
- DBUS_TYPE_STRING, &smode,
- DBUS_TYPE_INVALID))
- return bus_send_error_reply(connection, message, &error, -EINVAL);
-
- if (reload_if_possible && unit_can_reload(u)) {
- if (job_type == JOB_RESTART)
- job_type = JOB_RELOAD_OR_START;
- else if (job_type == JOB_TRY_RESTART)
- job_type = JOB_RELOAD;
- }
-
- if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
- dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
- return bus_send_error_reply(connection, message, &error, -EINVAL);
- }
-
- if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = job_dbus_path(j)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
- }
-
- if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
- }
-
- free(path);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- free(path);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
- Unit *u;
- int r;
- DBusMessage *reply;
-
- assert(connection);
- assert(message);
- assert(m);
-
- if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
- /* Be nice to gdbus and return introspection data for our mid-level paths */
-
- if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
- char *introspection = NULL;
- FILE *f;
- Iterator i;
- const char *k;
- size_t size;
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- /* We roll our own introspection code here, instead of
- * relying on bus_default_message_handler() because we
- * need to generate our introspection string
- * dynamically. */
-
- if (!(f = open_memstream(&introspection, &size)))
- goto oom;
-
- fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
- "<node>\n", f);
-
- fputs(BUS_INTROSPECTABLE_INTERFACE, f);
- fputs(BUS_PEER_INTERFACE, f);
-
- HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- char *p;
-
- if (k != u->id)
- continue;
-
- if (!(p = bus_path_escape(k))) {
- fclose(f);
- free(introspection);
- goto oom;
- }
-
- fprintf(f, "<node name=\"%s\"/>", p);
- free(p);
- }
-
- fputs("</node>\n", f);
-
- if (ferror(f)) {
- fclose(f);
- free(introspection);
- goto oom;
- }
-
- fclose(f);
-
- if (!introspection)
- goto oom;
-
- if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
- free(introspection);
- goto oom;
- }
-
- free(introspection);
-
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_HANDLED;
- }
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- if ((r = manager_get_unit_from_dbus_path(m, dbus_message_get_path(message), &u)) < 0) {
-
- if (r == -ENOMEM)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- if (r == -ENOENT) {
- DBusError e;
-
- dbus_error_init(&e);
- dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown unit");
- return bus_send_error_reply(connection, message, &e, r);
- }
-
- return bus_send_error_reply(connection, message, NULL, r);
- }
-
- return bus_unit_message_dispatch(u, connection, message);
-
-oom:
- if (reply)
- dbus_message_unref(reply);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-const DBusObjectPathVTable bus_unit_vtable = {
- .message_function = bus_unit_message_handler
-};
-
-void bus_unit_send_change_signal(Unit *u) {
- char *p = NULL;
- DBusMessage *m = NULL;
-
- assert(u);
-
- if (u->in_dbus_queue) {
- LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
- u->in_dbus_queue = false;
- }
-
- if (!u->id)
- return;
-
- if (!bus_has_subscriber(u->manager)) {
- u->sent_dbus_new_signal = true;
- return;
- }
-
- if (!(p = unit_dbus_path(u)))
- goto oom;
-
- if (u->sent_dbus_new_signal) {
- /* Send a properties changed signal. First for the
- * specific type, then for the generic unit. The
- * clients may rely on this order to get atomic
- * behaviour if needed. */
-
- if (UNIT_VTABLE(u)->bus_invalidating_properties) {
-
- if (!(m = bus_properties_changed_new(p,
- UNIT_VTABLE(u)->bus_interface,
- UNIT_VTABLE(u)->bus_invalidating_properties)))
- goto oom;
-
- if (bus_broadcast(u->manager, m) < 0)
- goto oom;
-
- dbus_message_unref(m);
- }
-
- if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
- goto oom;
-
- } else {
- /* Send a new signal */
-
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
- goto oom;
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &u->id,
- DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_INVALID))
- goto oom;
- }
-
- if (bus_broadcast(u->manager, m) < 0)
- goto oom;
-
- free(p);
- dbus_message_unref(m);
-
- u->sent_dbus_new_signal = true;
-
- return;
-
-oom:
- free(p);
-
- if (m)
- dbus_message_unref(m);
-
- log_error("Failed to allocate unit change/new signal.");
-}
-
-void bus_unit_send_removed_signal(Unit *u) {
- char *p = NULL;
- DBusMessage *m = NULL;
-
- assert(u);
-
- if (!bus_has_subscriber(u->manager))
- return;
-
- if (!u->sent_dbus_new_signal)
- bus_unit_send_change_signal(u);
-
- if (!u->id)
- return;
-
- if (!(p = unit_dbus_path(u)))
- goto oom;
-
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
- goto oom;
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &u->id,
- DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_INVALID))
- goto oom;
-
- if (bus_broadcast(u->manager, m) < 0)
- goto oom;
-
- free(p);
- dbus_message_unref(m);
-
- return;
-
-oom:
- free(p);
-
- if (m)
- dbus_message_unref(m);
-
- log_error("Failed to allocate unit remove signal.");
-}
-
-const BusProperty bus_unit_properties[] = {
- { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
- { "Names", bus_unit_append_names, "as", 0 },
- { "Following", bus_unit_append_following, "s", 0 },
- { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
- { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
- { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
- { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
- { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
- { "BindTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BIND_TO]), true },
- { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
- { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
- { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
- { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
- { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
- { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
- { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
- { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
- { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
- { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
- { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
- { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true },
- { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true },
- { "Description", bus_unit_append_description, "s", 0 },
- { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
- { "ActiveState", bus_unit_append_active_state, "s", 0 },
- { "SubState", bus_unit_append_sub_state, "s", 0 },
- { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
- { "UnitFileState", bus_unit_append_file_state, "s", 0 },
- { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
- { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
- { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
- { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
- { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
- { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
- { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
- { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
- { "CanStart", bus_unit_append_can_start, "b", 0 },
- { "CanStop", bus_unit_append_can_stop, "b", 0 },
- { "CanReload", bus_unit_append_can_reload, "b", 0 },
- { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
- { "Job", bus_unit_append_job, "(uo)", 0 },
- { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
- { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
- { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
- { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
- { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
- { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
- { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
- { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
- { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
- { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
- { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
- { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
- { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
- { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
- { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
- { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
- { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
- { NULL, }
-};
diff --git a/src/dbus-unit.h b/src/dbus-unit.h
deleted file mode 100644
index 4f19a80..0000000
--- a/src/dbus-unit.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbusunithfoo
-#define foodbusunithfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "manager.h"
-#include "dbus-common.h"
-
-#define BUS_UNIT_INTERFACE \
- " <interface name=\"org.freedesktop.systemd1.Unit\">\n" \
- " <method name=\"Start\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Stop\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Reload\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Restart\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"TryRestart\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReloadOrRestart\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"ReloadOrTryRestart\">\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"Kill\">\n" \
- " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"ResetFailed\"/>\n" \
- " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Names\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Requires\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"RequiresOverridable\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"BindTo\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"After\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"OnFailure\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Triggers\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"TriggeredBy\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"PropagateReloadTo\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"PropagateReloadFrom\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"SubState\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"UnitFileState\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"InactiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ActiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ActiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ActiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"InactiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"InactiveEnterTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"CanStart\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"CanStop\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"CanReload\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"CanIsolate\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"Job\" type=\"(uo)\" access=\"read\"/>\n" \
- " <property name=\"StopWhenUnneeded\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"RefuseManualStart\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"RefuseManualStop\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"AllowIsolate\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DefaultDependencies\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
- " <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
- " </interface>\n"
-
-#define BUS_UNIT_INTERFACES_LIST \
- BUS_GENERIC_INTERFACES_LIST \
- "org.freedesktop.systemd1.Unit\0"
-
-extern const BusProperty bus_unit_properties[];
-
-void bus_unit_send_change_signal(Unit *u);
-void bus_unit_send_removed_signal(Unit *u);
-
-extern const DBusObjectPathVTable bus_unit_vtable;
-
-extern const char bus_unit_interface[];
-
-#endif
diff --git a/src/dbus.c b/src/dbus.c
deleted file mode 100644
index ddf91f2..0000000
--- a/src/dbus.c
+++ /dev/null
@@ -1,1483 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <errno.h>
-#include <unistd.h>
-#include <dbus/dbus.h>
-
-#include "dbus.h"
-#include "log.h"
-#include "strv.h"
-#include "cgroup.h"
-#include "mkdir.h"
-#include "dbus-unit.h"
-#include "dbus-job.h"
-#include "dbus-manager.h"
-#include "dbus-service.h"
-#include "dbus-socket.h"
-#include "dbus-target.h"
-#include "dbus-device.h"
-#include "dbus-mount.h"
-#include "dbus-automount.h"
-#include "dbus-snapshot.h"
-#include "dbus-swap.h"
-#include "dbus-timer.h"
-#include "dbus-path.h"
-#include "bus-errors.h"
-#include "special.h"
-#include "dbus-common.h"
-
-#define CONNECTIONS_MAX 52
-
-/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */
-#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
-/* Only used as a fallback */
-#define DBUS_SESSION_BUS_DEFAULT_ADDRESS "autolaunch:"
-
-static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE;
-static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE;
-
-const char *const bus_interface_table[] = {
- "org.freedesktop.DBus.Properties", bus_properties_interface,
- "org.freedesktop.DBus.Introspectable", bus_introspectable_interface,
- "org.freedesktop.systemd1.Manager", bus_manager_interface,
- "org.freedesktop.systemd1.Job", bus_job_interface,
- "org.freedesktop.systemd1.Unit", bus_unit_interface,
- "org.freedesktop.systemd1.Service", bus_service_interface,
- "org.freedesktop.systemd1.Socket", bus_socket_interface,
- "org.freedesktop.systemd1.Target", bus_target_interface,
- "org.freedesktop.systemd1.Device", bus_device_interface,
- "org.freedesktop.systemd1.Mount", bus_mount_interface,
- "org.freedesktop.systemd1.Automount", bus_automount_interface,
- "org.freedesktop.systemd1.Snapshot", bus_snapshot_interface,
- "org.freedesktop.systemd1.Swap", bus_swap_interface,
- "org.freedesktop.systemd1.Timer", bus_timer_interface,
- "org.freedesktop.systemd1.Path", bus_path_interface,
- NULL
-};
-
-static void bus_done_api(Manager *m);
-static void bus_done_system(Manager *m);
-static void bus_done_private(Manager *m);
-static void shutdown_connection(Manager *m, DBusConnection *c);
-
-static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
- Manager *m = data;
-
- assert(bus);
- assert(m);
-
- /* We maintain two sets, one for those connections where we
- * requested a dispatch, and another where we didn't. And then,
- * we move the connections between the two sets. */
-
- if (status == DBUS_DISPATCH_COMPLETE)
- set_move_one(m->bus_connections, m->bus_connections_for_dispatch, bus);
- else
- set_move_one(m->bus_connections_for_dispatch, m->bus_connections, bus);
-}
-
-void bus_watch_event(Manager *m, Watch *w, int events) {
- assert(m);
- assert(w);
-
- /* This is called by the event loop whenever there is
- * something happening on D-Bus' file handles. */
-
- if (!dbus_watch_get_enabled(w->data.bus_watch))
- return;
-
- dbus_watch_handle(w->data.bus_watch, bus_events_to_flags(events));
-}
-
-static dbus_bool_t bus_add_watch(DBusWatch *bus_watch, void *data) {
- Manager *m = data;
- Watch *w;
- struct epoll_event ev;
-
- assert(bus_watch);
- assert(m);
-
- if (!(w = new0(Watch, 1)))
- return FALSE;
-
- w->fd = dbus_watch_get_unix_fd(bus_watch);
- w->type = WATCH_DBUS_WATCH;
- w->data.bus_watch = bus_watch;
-
- zero(ev);
- ev.events = bus_flags_to_events(bus_watch);
- ev.data.ptr = w;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
-
- if (errno != EEXIST) {
- free(w);
- return FALSE;
- }
-
- /* Hmm, bloody D-Bus creates multiple watches on the
- * same fd. epoll() does not like that. As a dirty
- * hack we simply dup() the fd and hence get a second
- * one we can safely add to the epoll(). */
-
- if ((w->fd = dup(w->fd)) < 0) {
- free(w);
- return FALSE;
- }
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
- close_nointr_nofail(w->fd);
- free(w);
- return FALSE;
- }
-
- w->fd_is_dupped = true;
- }
-
- dbus_watch_set_data(bus_watch, w, NULL);
-
- return TRUE;
-}
-
-static void bus_remove_watch(DBusWatch *bus_watch, void *data) {
- Manager *m = data;
- Watch *w;
-
- assert(bus_watch);
- assert(m);
-
- w = dbus_watch_get_data(bus_watch);
- if (!w)
- return;
-
- assert(w->type == WATCH_DBUS_WATCH);
- assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
-
- if (w->fd_is_dupped)
- close_nointr_nofail(w->fd);
-
- free(w);
-}
-
-static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
- Manager *m = data;
- Watch *w;
- struct epoll_event ev;
-
- assert(bus_watch);
- assert(m);
-
- w = dbus_watch_get_data(bus_watch);
- if (!w)
- return;
-
- assert(w->type == WATCH_DBUS_WATCH);
-
- zero(ev);
- ev.events = bus_flags_to_events(bus_watch);
- ev.data.ptr = w;
-
- assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_MOD, w->fd, &ev) == 0);
-}
-
-static int bus_timeout_arm(Manager *m, Watch *w) {
- struct itimerspec its;
-
- assert(m);
- assert(w);
-
- zero(its);
-
- if (dbus_timeout_get_enabled(w->data.bus_timeout)) {
- timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC);
- its.it_interval = its.it_value;
- }
-
- if (timerfd_settime(w->fd, 0, &its, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
-void bus_timeout_event(Manager *m, Watch *w, int events) {
- assert(m);
- assert(w);
-
- /* This is called by the event loop whenever there is
- * something happening on D-Bus' file handles. */
-
- if (!(dbus_timeout_get_enabled(w->data.bus_timeout)))
- return;
-
- dbus_timeout_handle(w->data.bus_timeout);
-}
-
-static dbus_bool_t bus_add_timeout(DBusTimeout *timeout, void *data) {
- Manager *m = data;
- Watch *w;
- struct epoll_event ev;
-
- assert(timeout);
- assert(m);
-
- if (!(w = new0(Watch, 1)))
- return FALSE;
-
- if ((w->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
- goto fail;
-
- w->type = WATCH_DBUS_TIMEOUT;
- w->data.bus_timeout = timeout;
-
- if (bus_timeout_arm(m, w) < 0)
- goto fail;
-
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = w;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
- goto fail;
-
- dbus_timeout_set_data(timeout, w, NULL);
-
- return TRUE;
-
-fail:
- if (w->fd >= 0)
- close_nointr_nofail(w->fd);
-
- free(w);
- return FALSE;
-}
-
-static void bus_remove_timeout(DBusTimeout *timeout, void *data) {
- Manager *m = data;
- Watch *w;
-
- assert(timeout);
- assert(m);
-
- w = dbus_timeout_get_data(timeout);
- if (!w)
- return;
-
- assert(w->type == WATCH_DBUS_TIMEOUT);
-
- assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
- close_nointr_nofail(w->fd);
- free(w);
-}
-
-static void bus_toggle_timeout(DBusTimeout *timeout, void *data) {
- Manager *m = data;
- Watch *w;
- int r;
-
- assert(timeout);
- assert(m);
-
- w = dbus_timeout_get_data(timeout);
- if (!w)
- return;
-
- assert(w->type == WATCH_DBUS_TIMEOUT);
-
- if ((r = bus_timeout_arm(m, w)) < 0)
- log_error("Failed to rearm timer: %s", strerror(-r));
-}
-
-static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
- DBusError error;
- DBusMessage *reply = NULL;
-
- assert(connection);
- assert(message);
- assert(m);
-
- dbus_error_init(&error);
-
- if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
- dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
- log_debug("Got D-Bus request: %s.%s() on %s",
- dbus_message_get_interface(message),
- dbus_message_get_member(message),
- dbus_message_get_path(message));
-
- if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
- log_debug("API D-Bus connection terminated.");
- bus_done_api(m);
-
- } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
- const char *name, *old_owner, *new_owner;
-
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &old_owner,
- DBUS_TYPE_STRING, &new_owner,
- DBUS_TYPE_INVALID))
- log_error("Failed to parse NameOwnerChanged message: %s", bus_error_message(&error));
- else {
- if (set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) name))
- log_debug("Subscription client vanished: %s (left: %u)", name, set_size(BUS_CONNECTION_SUBSCRIBED(m, connection)));
-
- if (old_owner[0] == 0)
- old_owner = NULL;
-
- if (new_owner[0] == 0)
- new_owner = NULL;
-
- manager_dispatch_bus_name_owner_changed(m, name, old_owner, new_owner);
- }
- } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Activator", "ActivationRequest")) {
- const char *name;
-
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID))
- log_error("Failed to parse ActivationRequest message: %s", bus_error_message(&error));
- else {
- int r;
- Unit *u;
-
- log_debug("Got D-Bus activation request for %s", name);
-
- if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) ||
- manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) {
- r = -EADDRNOTAVAIL;
- dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
- } else {
- r = manager_load_unit(m, name, NULL, &error, &u);
-
- if (r >= 0 && u->refuse_manual_start)
- r = -EPERM;
-
- if (r >= 0)
- r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL);
- }
-
- if (r < 0) {
- const char *id, *text;
-
- log_debug("D-Bus activation failed for %s: %s", name, strerror(-r));
-
- if (!(reply = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Activator", "ActivationFailure")))
- goto oom;
-
- id = error.name ? error.name : bus_errno_to_dbus(r);
- text = bus_error(&error, r);
-
- if (!dbus_message_set_destination(reply, DBUS_SERVICE_DBUS) ||
- !dbus_message_append_args(reply,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &id,
- DBUS_TYPE_STRING, &text,
- DBUS_TYPE_INVALID))
- goto oom;
- }
-
- /* On success we don't do anything, the service will be spawned now */
- }
- }
-
- dbus_error_free(&error);
-
- if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
-
- dbus_message_unref(reply);
- }
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-oom:
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
- DBusError error;
-
- assert(connection);
- assert(message);
- assert(m);
-
- dbus_error_init(&error);
-
- if (m->api_bus != m->system_bus &&
- (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
- dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL))
- log_debug("Got D-Bus request on system bus: %s.%s() on %s",
- dbus_message_get_interface(message),
- dbus_message_get_member(message),
- dbus_message_get_path(message));
-
- if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
- log_debug("System D-Bus connection terminated.");
- bus_done_system(m);
-
- } else if (m->running_as != MANAGER_SYSTEM &&
- dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
-
- const char *cgroup;
-
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &cgroup,
- DBUS_TYPE_INVALID))
- log_error("Failed to parse Released message: %s", bus_error_message(&error));
- else
- cgroup_notify_empty(m, cgroup);
- }
-
- dbus_error_free(&error);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static DBusHandlerResult private_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
- Manager *m = data;
- DBusError error;
-
- assert(connection);
- assert(message);
- assert(m);
-
- dbus_error_init(&error);
-
- if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL ||
- dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
- log_debug("Got D-Bus request: %s.%s() on %s",
- dbus_message_get_interface(message),
- dbus_message_get_member(message),
- dbus_message_get_path(message));
-
- if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected"))
- shutdown_connection(m, connection);
- else if (m->running_as == MANAGER_SYSTEM &&
- dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
-
- const char *cgroup;
-
- if (!dbus_message_get_args(message, &error,
- DBUS_TYPE_STRING, &cgroup,
- DBUS_TYPE_INVALID))
- log_error("Failed to parse Released message: %s", bus_error_message(&error));
- else
- cgroup_notify_empty(m, cgroup);
-
- /* Forward the message to the system bus, so that user
- * instances are notified as well */
-
- if (m->system_bus)
- dbus_connection_send(m->system_bus, message, NULL);
- }
-
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-unsigned bus_dispatch(Manager *m) {
- DBusConnection *c;
-
- assert(m);
-
- if (m->queued_message) {
- /* If we cannot get rid of this message we won't
- * dispatch any D-Bus messages, so that we won't end
- * up wanting to queue another message. */
-
- if (m->queued_message_connection)
- if (!dbus_connection_send(m->queued_message_connection, m->queued_message, NULL))
- return 0;
-
- dbus_message_unref(m->queued_message);
- m->queued_message = NULL;
- m->queued_message_connection = NULL;
- }
-
- if ((c = set_first(m->bus_connections_for_dispatch))) {
- if (dbus_connection_dispatch(c) == DBUS_DISPATCH_COMPLETE)
- set_move_one(m->bus_connections, m->bus_connections_for_dispatch, c);
-
- return 1;
- }
-
- return 0;
-}
-
-static void request_name_pending_cb(DBusPendingCall *pending, void *userdata) {
- DBusMessage *reply;
- DBusError error;
-
- dbus_error_init(&error);
-
- assert_se(reply = dbus_pending_call_steal_reply(pending));
-
- switch (dbus_message_get_type(reply)) {
-
- case DBUS_MESSAGE_TYPE_ERROR:
-
- assert_se(dbus_set_error_from_message(&error, reply));
- log_warning("RequestName() failed: %s", bus_error_message(&error));
- break;
-
- case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
- uint32_t r;
-
- if (!dbus_message_get_args(reply,
- &error,
- DBUS_TYPE_UINT32, &r,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse RequestName() reply: %s", bus_error_message(&error));
- break;
- }
-
- if (r == 1)
- log_debug("Successfully acquired name.");
- else
- log_error("Name already owned.");
-
- break;
- }
-
- default:
- assert_not_reached("Invalid reply message");
- }
-
- dbus_message_unref(reply);
- dbus_error_free(&error);
-}
-
-static int request_name(Manager *m) {
- const char *name = "org.freedesktop.systemd1";
- /* Allow replacing of our name, to ease implementation of
- * reexecution, where we keep the old connection open until
- * after the new connection is set up and the name installed
- * to allow clients to synchronously wait for reexecution to
- * finish */
- uint32_t flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT|DBUS_NAME_FLAG_REPLACE_EXISTING;
- DBusMessage *message = NULL;
- DBusPendingCall *pending = NULL;
-
- if (!(message = dbus_message_new_method_call(
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- "RequestName")))
- goto oom;
-
- if (!dbus_message_append_args(
- message,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_UINT32, &flags,
- DBUS_TYPE_INVALID))
- goto oom;
-
- if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
- goto oom;
-
- if (!dbus_pending_call_set_notify(pending, request_name_pending_cb, m, NULL))
- goto oom;
-
- dbus_message_unref(message);
- dbus_pending_call_unref(pending);
-
- /* We simple ask for the name and don't wait for it. Sooner or
- * later we'll have it. */
-
- return 0;
-
-oom:
- if (pending) {
- dbus_pending_call_cancel(pending);
- dbus_pending_call_unref(pending);
- }
-
- if (message)
- dbus_message_unref(message);
-
- return -ENOMEM;
-}
-
-static void query_name_list_pending_cb(DBusPendingCall *pending, void *userdata) {
- DBusMessage *reply;
- DBusError error;
- Manager *m = userdata;
-
- assert(m);
-
- dbus_error_init(&error);
-
- assert_se(reply = dbus_pending_call_steal_reply(pending));
-
- switch (dbus_message_get_type(reply)) {
-
- case DBUS_MESSAGE_TYPE_ERROR:
-
- assert_se(dbus_set_error_from_message(&error, reply));
- log_warning("ListNames() failed: %s", bus_error_message(&error));
- break;
-
- case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
- int r;
- char **l;
-
- if ((r = bus_parse_strv(reply, &l)) < 0)
- log_warning("Failed to parse ListNames() reply: %s", strerror(-r));
- else {
- char **t;
-
- STRV_FOREACH(t, l)
- /* This is a bit hacky, we say the
- * owner of the name is the name
- * itself, because we don't want the
- * extra traffic to figure out the
- * real owner. */
- manager_dispatch_bus_name_owner_changed(m, *t, NULL, *t);
-
- strv_free(l);
- }
-
- break;
- }
-
- default:
- assert_not_reached("Invalid reply message");
- }
-
- dbus_message_unref(reply);
- dbus_error_free(&error);
-}
-
-static int query_name_list(Manager *m) {
- DBusMessage *message = NULL;
- DBusPendingCall *pending = NULL;
-
- /* Asks for the currently installed bus names */
-
- if (!(message = dbus_message_new_method_call(
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- "ListNames")))
- goto oom;
-
- if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
- goto oom;
-
- if (!dbus_pending_call_set_notify(pending, query_name_list_pending_cb, m, NULL))
- goto oom;
-
- dbus_message_unref(message);
- dbus_pending_call_unref(pending);
-
- /* We simple ask for the list and don't wait for it. Sooner or
- * later we'll get it. */
-
- return 0;
-
-oom:
- if (pending) {
- dbus_pending_call_cancel(pending);
- dbus_pending_call_unref(pending);
- }
-
- if (message)
- dbus_message_unref(message);
-
- return -ENOMEM;
-}
-
-static int bus_setup_loop(Manager *m, DBusConnection *bus) {
- assert(m);
- assert(bus);
-
- dbus_connection_set_exit_on_disconnect(bus, FALSE);
-
- if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
- !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) {
- log_error("Not enough memory");
- return -ENOMEM;
- }
-
- if (set_put(m->bus_connections_for_dispatch, bus) < 0) {
- log_error("Not enough memory");
- return -ENOMEM;
- }
-
- dbus_connection_set_dispatch_status_function(bus, bus_dispatch_status, m, NULL);
- return 0;
-}
-
-static dbus_bool_t allow_only_same_user(DBusConnection *connection, unsigned long uid, void *data) {
- return uid == 0 || uid == geteuid();
-}
-
-static void bus_new_connection(
- DBusServer *server,
- DBusConnection *new_connection,
- void *data) {
-
- Manager *m = data;
-
- assert(m);
-
- if (set_size(m->bus_connections) >= CONNECTIONS_MAX) {
- log_error("Too many concurrent connections.");
- return;
- }
-
- dbus_connection_set_unix_user_function(new_connection, allow_only_same_user, NULL, NULL);
-
- if (bus_setup_loop(m, new_connection) < 0)
- return;
-
- if (!dbus_connection_register_object_path(new_connection, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
- !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
- !dbus_connection_register_fallback(new_connection, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
- !dbus_connection_add_filter(new_connection, private_bus_message_filter, m, NULL)) {
- log_error("Not enough memory.");
- return;
- }
-
- log_debug("Accepted connection on private bus.");
-
- dbus_connection_ref(new_connection);
-}
-
-static int init_registered_system_bus(Manager *m) {
- char *id;
-
- if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
- log_error("Not enough memory");
- return -ENOMEM;
- }
-
- if (m->running_as != MANAGER_SYSTEM) {
- DBusError error;
-
- dbus_error_init(&error);
-
- dbus_bus_add_match(m->system_bus,
- "type='signal',"
- "interface='org.freedesktop.systemd1.Agent',"
- "member='Released',"
- "path='/org/freedesktop/systemd1/agent'",
- &error);
-
- if (dbus_error_is_set(&error)) {
- log_error("Failed to register match: %s", bus_error_message(&error));
- dbus_error_free(&error);
- return -1;
- }
- }
-
- log_debug("Successfully connected to system D-Bus bus %s as %s",
- strnull((id = dbus_connection_get_server_id(m->system_bus))),
- strnull(dbus_bus_get_unique_name(m->system_bus)));
- dbus_free(id);
-
- return 0;
-}
-
-static int init_registered_api_bus(Manager *m) {
- int r;
-
- if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
- !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
- !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
- !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) {
- log_error("Not enough memory");
- return -ENOMEM;
- }
-
- /* Get NameOwnerChange messages */
- dbus_bus_add_match(m->api_bus,
- "type='signal',"
- "sender='"DBUS_SERVICE_DBUS"',"
- "interface='"DBUS_INTERFACE_DBUS"',"
- "member='NameOwnerChanged',"
- "path='"DBUS_PATH_DBUS"'",
- NULL);
-
- /* Get activation requests */
- dbus_bus_add_match(m->api_bus,
- "type='signal',"
- "sender='"DBUS_SERVICE_DBUS"',"
- "interface='org.freedesktop.systemd1.Activator',"
- "member='ActivationRequest',"
- "path='"DBUS_PATH_DBUS"'",
- NULL);
-
- r = request_name(m);
- if (r < 0)
- return r;
-
- r = query_name_list(m);
- if (r < 0)
- return r;
-
- if (m->running_as == MANAGER_USER) {
- char *id;
- log_debug("Successfully connected to API D-Bus bus %s as %s",
- strnull((id = dbus_connection_get_server_id(m->api_bus))),
- strnull(dbus_bus_get_unique_name(m->api_bus)));
- dbus_free(id);
- } else
- log_debug("Successfully initialized API on the system bus");
-
- return 0;
-}
-
-static void bus_register_cb(DBusPendingCall *pending, void *userdata) {
- Manager *m = userdata;
- DBusConnection **conn;
- DBusMessage *reply;
- DBusError error;
- const char *name;
- int r = 0;
-
- dbus_error_init(&error);
-
- conn = dbus_pending_call_get_data(pending, m->conn_data_slot);
- assert(conn == &m->system_bus || conn == &m->api_bus);
-
- reply = dbus_pending_call_steal_reply(pending);
-
- switch (dbus_message_get_type(reply)) {
- case DBUS_MESSAGE_TYPE_ERROR:
- assert_se(dbus_set_error_from_message(&error, reply));
- log_warning("Failed to register to bus: %s", bus_error_message(&error));
- r = -1;
- break;
- case DBUS_MESSAGE_TYPE_METHOD_RETURN:
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse Hello reply: %s", bus_error_message(&error));
- r = -1;
- break;
- }
-
- log_debug("Received name %s in reply to Hello", name);
- if (!dbus_bus_set_unique_name(*conn, name)) {
- log_error("Failed to set unique name");
- r = -1;
- break;
- }
-
- if (conn == &m->system_bus) {
- r = init_registered_system_bus(m);
- if (r == 0 && m->running_as == MANAGER_SYSTEM)
- r = init_registered_api_bus(m);
- } else
- r = init_registered_api_bus(m);
-
- break;
- default:
- assert_not_reached("Invalid reply message");
- }
-
- dbus_message_unref(reply);
- dbus_error_free(&error);
-
- if (r < 0) {
- if (conn == &m->system_bus) {
- log_debug("Failed setting up the system bus");
- bus_done_system(m);
- } else {
- log_debug("Failed setting up the API bus");
- bus_done_api(m);
- }
- }
-}
-
-static int manager_bus_async_register(Manager *m, DBusConnection **conn) {
- DBusMessage *message = NULL;
- DBusPendingCall *pending = NULL;
-
- message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- "Hello");
- if (!message)
- goto oom;
-
- if (!dbus_connection_send_with_reply(*conn, message, &pending, -1))
- goto oom;
-
- if (!dbus_pending_call_set_data(pending, m->conn_data_slot, conn, NULL))
- goto oom;
-
- if (!dbus_pending_call_set_notify(pending, bus_register_cb, m, NULL))
- goto oom;
-
- dbus_message_unref(message);
- dbus_pending_call_unref(pending);
-
- return 0;
-oom:
- if (pending) {
- dbus_pending_call_cancel(pending);
- dbus_pending_call_unref(pending);
- }
-
- if (message)
- dbus_message_unref(message);
-
- return -ENOMEM;
-}
-
-static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type) {
- const char *address;
- DBusConnection *connection;
- DBusError error;
-
- switch (type) {
- case DBUS_BUS_SYSTEM:
- address = getenv("DBUS_SYSTEM_BUS_ADDRESS");
- if (!address || !address[0])
- address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
- break;
- case DBUS_BUS_SESSION:
- address = getenv("DBUS_SESSION_BUS_ADDRESS");
- if (!address || !address[0])
- address = DBUS_SESSION_BUS_DEFAULT_ADDRESS;
- break;
- default:
- assert_not_reached("Invalid bus type");
- }
-
- dbus_error_init(&error);
-
- connection = dbus_connection_open_private(address, &error);
- if (!connection) {
- log_warning("Failed to open private bus connection: %s", bus_error_message(&error));
- goto fail;
- }
-
- return connection;
-fail:
- if (connection)
- dbus_connection_close(connection);
- dbus_error_free(&error);
- return NULL;
-}
-
-static int bus_init_system(Manager *m) {
- int r;
-
- if (m->system_bus)
- return 0;
-
- m->system_bus = manager_bus_connect_private(m, DBUS_BUS_SYSTEM);
- if (!m->system_bus) {
- log_debug("Failed to connect to system D-Bus, retrying later");
- r = 0;
- goto fail;
- }
-
- r = bus_setup_loop(m, m->system_bus);
- if (r < 0)
- goto fail;
-
- r = manager_bus_async_register(m, &m->system_bus);
- if (r < 0)
- goto fail;
-
- return 0;
-fail:
- bus_done_system(m);
-
- return r;
-}
-
-static int bus_init_api(Manager *m) {
- int r;
-
- if (m->api_bus)
- return 0;
-
- if (m->running_as == MANAGER_SYSTEM) {
- m->api_bus = m->system_bus;
- /* In this mode there is no distinct connection to the API bus,
- * the API is published on the system bus.
- * bus_register_cb() is aware of that and will init the API
- * when the system bus gets registered.
- * No need to setup anything here. */
- return 0;
- }
-
- m->api_bus = manager_bus_connect_private(m, DBUS_BUS_SESSION);
- if (!m->api_bus) {
- log_debug("Failed to connect to API D-Bus, retrying later");
- r = 0;
- goto fail;
- }
-
- r = bus_setup_loop(m, m->api_bus);
- if (r < 0)
- goto fail;
-
- r = manager_bus_async_register(m, &m->api_bus);
- if (r < 0)
- goto fail;
-
- return 0;
-fail:
- bus_done_api(m);
-
- return r;
-}
-
-static int bus_init_private(Manager *m) {
- DBusError error;
- int r;
- const char *const external_only[] = {
- "EXTERNAL",
- NULL
- };
-
- assert(m);
-
- dbus_error_init(&error);
-
- if (m->private_bus)
- return 0;
-
- if (m->running_as == MANAGER_SYSTEM) {
-
- /* We want the private bus only when running as init */
- if (getpid() != 1)
- return 0;
-
- unlink("/run/systemd/private");
- m->private_bus = dbus_server_listen("unix:path=/run/systemd/private", &error);
- } else {
- const char *e;
- char *p;
-
- e = getenv("XDG_RUNTIME_DIR");
- if (!e)
- return 0;
-
- if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) {
- log_error("Not enough memory");
- r = -ENOMEM;
- goto fail;
- }
-
- mkdir_parents(p+10, 0755);
- unlink(p+10);
- m->private_bus = dbus_server_listen(p, &error);
- free(p);
- }
-
- if (!m->private_bus) {
- log_error("Failed to create private D-Bus server: %s", bus_error_message(&error));
- r = -EIO;
- goto fail;
- }
-
- if (!dbus_server_set_auth_mechanisms(m->private_bus, (const char**) external_only) ||
- !dbus_server_set_watch_functions(m->private_bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
- !dbus_server_set_timeout_functions(m->private_bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) {
- log_error("Not enough memory");
- r = -ENOMEM;
- goto fail;
- }
-
- dbus_server_set_new_connection_function(m->private_bus, bus_new_connection, m, NULL);
-
- log_debug("Successfully created private D-Bus server.");
-
- return 0;
-
-fail:
- bus_done_private(m);
- dbus_error_free(&error);
-
- return r;
-}
-
-int bus_init(Manager *m, bool try_bus_connect) {
- int r;
-
- if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 ||
- set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0)
- goto oom;
-
- if (m->name_data_slot < 0)
- if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot))
- goto oom;
-
- if (m->conn_data_slot < 0)
- if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot))
- goto oom;
-
- if (m->subscribed_data_slot < 0)
- if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot))
- goto oom;
-
- if (try_bus_connect) {
- if ((r = bus_init_system(m)) < 0 ||
- (r = bus_init_api(m)) < 0)
- return r;
- }
-
- if ((r = bus_init_private(m)) < 0)
- return r;
-
- return 0;
-oom:
- log_error("Not enough memory");
- return -ENOMEM;
-}
-
-static void shutdown_connection(Manager *m, DBusConnection *c) {
- Set *s;
- Job *j;
- Iterator i;
-
- HASHMAP_FOREACH(j, m->jobs, i)
- if (j->bus == c) {
- free(j->bus_client);
- j->bus_client = NULL;
-
- j->bus = NULL;
- }
-
- set_remove(m->bus_connections, c);
- set_remove(m->bus_connections_for_dispatch, c);
-
- if ((s = BUS_CONNECTION_SUBSCRIBED(m, c))) {
- char *t;
-
- while ((t = set_steal_first(s)))
- free(t);
-
- set_free(s);
- }
-
- if (m->queued_message_connection == c) {
- m->queued_message_connection = NULL;
-
- if (m->queued_message) {
- dbus_message_unref(m->queued_message);
- m->queued_message = NULL;
- }
- }
-
- dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL);
- /* system manager cannot afford to block on DBus */
- if (m->running_as != MANAGER_SYSTEM)
- dbus_connection_flush(c);
- dbus_connection_close(c);
- dbus_connection_unref(c);
-}
-
-static void bus_done_api(Manager *m) {
- if (!m->api_bus)
- return;
-
- if (m->running_as == MANAGER_USER)
- shutdown_connection(m, m->api_bus);
-
- m->api_bus = NULL;
-
- if (m->queued_message) {
- dbus_message_unref(m->queued_message);
- m->queued_message = NULL;
- }
-}
-
-static void bus_done_system(Manager *m) {
- if (!m->system_bus)
- return;
-
- if (m->running_as == MANAGER_SYSTEM)
- bus_done_api(m);
-
- shutdown_connection(m, m->system_bus);
- m->system_bus = NULL;
-}
-
-static void bus_done_private(Manager *m) {
- if (!m->private_bus)
- return;
-
- dbus_server_disconnect(m->private_bus);
- dbus_server_unref(m->private_bus);
- m->private_bus = NULL;
-}
-
-void bus_done(Manager *m) {
- DBusConnection *c;
-
- bus_done_api(m);
- bus_done_system(m);
- bus_done_private(m);
-
- while ((c = set_steal_first(m->bus_connections)))
- shutdown_connection(m, c);
-
- while ((c = set_steal_first(m->bus_connections_for_dispatch)))
- shutdown_connection(m, c);
-
- set_free(m->bus_connections);
- set_free(m->bus_connections_for_dispatch);
-
- if (m->name_data_slot >= 0)
- dbus_pending_call_free_data_slot(&m->name_data_slot);
-
- if (m->conn_data_slot >= 0)
- dbus_pending_call_free_data_slot(&m->conn_data_slot);
-
- if (m->subscribed_data_slot >= 0)
- dbus_connection_free_data_slot(&m->subscribed_data_slot);
-}
-
-static void query_pid_pending_cb(DBusPendingCall *pending, void *userdata) {
- Manager *m = userdata;
- DBusMessage *reply;
- DBusError error;
- const char *name;
-
- dbus_error_init(&error);
-
- assert_se(name = BUS_PENDING_CALL_NAME(m, pending));
- assert_se(reply = dbus_pending_call_steal_reply(pending));
-
- switch (dbus_message_get_type(reply)) {
-
- case DBUS_MESSAGE_TYPE_ERROR:
-
- assert_se(dbus_set_error_from_message(&error, reply));
- log_warning("GetConnectionUnixProcessID() failed: %s", bus_error_message(&error));
- break;
-
- case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
- uint32_t r;
-
- if (!dbus_message_get_args(reply,
- &error,
- DBUS_TYPE_UINT32, &r,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse GetConnectionUnixProcessID() reply: %s", bus_error_message(&error));
- break;
- }
-
- manager_dispatch_bus_query_pid_done(m, name, (pid_t) r);
- break;
- }
-
- default:
- assert_not_reached("Invalid reply message");
- }
-
- dbus_message_unref(reply);
- dbus_error_free(&error);
-}
-
-int bus_query_pid(Manager *m, const char *name) {
- DBusMessage *message = NULL;
- DBusPendingCall *pending = NULL;
- char *n = NULL;
-
- assert(m);
- assert(name);
-
- if (!(message = dbus_message_new_method_call(
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- "GetConnectionUnixProcessID")))
- goto oom;
-
- if (!(dbus_message_append_args(
- message,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)))
- goto oom;
-
- if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1))
- goto oom;
-
- if (!(n = strdup(name)))
- goto oom;
-
- if (!dbus_pending_call_set_data(pending, m->name_data_slot, n, free))
- goto oom;
-
- n = NULL;
-
- if (!dbus_pending_call_set_notify(pending, query_pid_pending_cb, m, NULL))
- goto oom;
-
- dbus_message_unref(message);
- dbus_pending_call_unref(pending);
-
- return 0;
-
-oom:
- free(n);
-
- if (pending) {
- dbus_pending_call_cancel(pending);
- dbus_pending_call_unref(pending);
- }
-
- if (message)
- dbus_message_unref(message);
-
- return -ENOMEM;
-}
-
-int bus_broadcast(Manager *m, DBusMessage *message) {
- bool oom = false;
- Iterator i;
- DBusConnection *c;
-
- assert(m);
- assert(message);
-
- SET_FOREACH(c, m->bus_connections_for_dispatch, i)
- if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
- oom = !dbus_connection_send(c, message, NULL);
-
- SET_FOREACH(c, m->bus_connections, i)
- if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
- oom = !dbus_connection_send(c, message, NULL);
-
- return oom ? -ENOMEM : 0;
-}
-
-bool bus_has_subscriber(Manager *m) {
- Iterator i;
- DBusConnection *c;
-
- assert(m);
-
- SET_FOREACH(c, m->bus_connections_for_dispatch, i)
- if (bus_connection_has_subscriber(m, c))
- return true;
-
- SET_FOREACH(c, m->bus_connections, i)
- if (bus_connection_has_subscriber(m, c))
- return true;
-
- return false;
-}
-
-bool bus_connection_has_subscriber(Manager *m, DBusConnection *c) {
- assert(m);
- assert(c);
-
- return !set_isempty(BUS_CONNECTION_SUBSCRIBED(m, c));
-}
-
-int bus_fdset_add_all(Manager *m, FDSet *fds) {
- Iterator i;
- DBusConnection *c;
-
- assert(m);
- assert(fds);
-
- /* When we are about to reexecute we add all D-Bus fds to the
- * set to pass over to the newly executed systemd. They won't
- * be used there however, except that they are closed at the
- * very end of deserialization, those making it possible for
- * clients to synchronously wait for systemd to reexec by
- * simply waiting for disconnection */
-
- SET_FOREACH(c, m->bus_connections_for_dispatch, i) {
- int fd;
-
- if (dbus_connection_get_unix_fd(c, &fd)) {
- fd = fdset_put_dup(fds, fd);
-
- if (fd < 0)
- return fd;
- }
- }
-
- SET_FOREACH(c, m->bus_connections, i) {
- int fd;
-
- if (dbus_connection_get_unix_fd(c, &fd)) {
- fd = fdset_put_dup(fds, fd);
-
- if (fd < 0)
- return fd;
- }
- }
-
- return 0;
-}
-
-void bus_broadcast_finished(
- Manager *m,
- usec_t kernel_usec,
- usec_t initrd_usec,
- usec_t userspace_usec,
- usec_t total_usec) {
-
- DBusMessage *message;
-
- assert(m);
-
- message = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished");
- if (!message) {
- log_error("Out of memory.");
- return;
- }
-
- assert_cc(sizeof(usec_t) == sizeof(uint64_t));
- if (!dbus_message_append_args(message,
- DBUS_TYPE_UINT64, &kernel_usec,
- DBUS_TYPE_UINT64, &initrd_usec,
- DBUS_TYPE_UINT64, &userspace_usec,
- DBUS_TYPE_UINT64, &total_usec,
- DBUS_TYPE_INVALID)) {
- log_error("Out of memory.");
- goto finish;
- }
-
-
- if (bus_broadcast(m, message) < 0) {
- log_error("Out of memory.");
- goto finish;
- }
-
-finish:
- if (message)
- dbus_message_unref(message);
-}
diff --git a/src/dbus.h b/src/dbus.h
deleted file mode 100644
index bd539d0..0000000
--- a/src/dbus.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodbushfoo
-#define foodbushfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include "manager.h"
-
-int bus_init(Manager *m, bool try_bus_connect);
-void bus_done(Manager *m);
-
-unsigned bus_dispatch(Manager *m);
-
-void bus_watch_event(Manager *m, Watch *w, int events);
-void bus_timeout_event(Manager *m, Watch *w, int events);
-
-int bus_query_pid(Manager *m, const char *name);
-
-int bus_broadcast(Manager *m, DBusMessage *message);
-
-bool bus_has_subscriber(Manager *m);
-bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
-
-int bus_fdset_add_all(Manager *m, FDSet *fds);
-
-void bus_broadcast_finished(Manager *m, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
-
-#define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
-#define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
-
-extern const char * const bus_interface_table[];
-
-#endif
diff --git a/src/device.c b/src/device.c
deleted file mode 100644
index 0575379..0000000
--- a/src/device.c
+++ /dev/null
@@ -1,616 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <sys/epoll.h>
-#include <libudev.h>
-
-#include "unit.h"
-#include "device.h"
-#include "strv.h"
-#include "log.h"
-#include "unit-name.h"
-#include "dbus-device.h"
-#include "def.h"
-
-static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
- [DEVICE_DEAD] = UNIT_INACTIVE,
- [DEVICE_PLUGGED] = UNIT_ACTIVE
-};
-
-static void device_unset_sysfs(Device *d) {
- Device *first;
-
- assert(d);
-
- if (!d->sysfs)
- return;
-
- /* Remove this unit from the chain of devices which share the
- * same sysfs path. */
- first = hashmap_get(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
- LIST_REMOVE(Device, same_sysfs, first, d);
-
- if (first)
- hashmap_remove_and_replace(UNIT(d)->manager->devices_by_sysfs, d->sysfs, first->sysfs, first);
- else
- hashmap_remove(UNIT(d)->manager->devices_by_sysfs, d->sysfs);
-
- free(d->sysfs);
- d->sysfs = NULL;
-}
-
-static void device_init(Unit *u) {
- Device *d = DEVICE(u);
-
- assert(d);
- assert(UNIT(d)->load_state == UNIT_STUB);
-
- /* In contrast to all other unit types we timeout jobs waiting
- * for devices by default. This is because they otherwise wait
- * indefinitely for plugged in devices, something which cannot
- * happen for the other units since their operations time out
- * anyway. */
- UNIT(d)->job_timeout = DEFAULT_TIMEOUT_USEC;
-
- UNIT(d)->ignore_on_isolate = true;
- UNIT(d)->ignore_on_snapshot = true;
-}
-
-static void device_done(Unit *u) {
- Device *d = DEVICE(u);
-
- assert(d);
-
- device_unset_sysfs(d);
-}
-
-static void device_set_state(Device *d, DeviceState state) {
- DeviceState old_state;
- assert(d);
-
- old_state = d->state;
- d->state = state;
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(d)->id,
- device_state_to_string(old_state),
- device_state_to_string(state));
-
- unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int device_coldplug(Unit *u) {
- Device *d = DEVICE(u);
-
- assert(d);
- assert(d->state == DEVICE_DEAD);
-
- if (d->sysfs)
- device_set_state(d, DEVICE_PLUGGED);
-
- return 0;
-}
-
-static void device_dump(Unit *u, FILE *f, const char *prefix) {
- Device *d = DEVICE(u);
-
- assert(d);
-
- fprintf(f,
- "%sDevice State: %s\n"
- "%sSysfs Path: %s\n",
- prefix, device_state_to_string(d->state),
- prefix, strna(d->sysfs));
-}
-
-static UnitActiveState device_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[DEVICE(u)->state];
-}
-
-static const char *device_sub_state_to_string(Unit *u) {
- assert(u);
-
- return device_state_to_string(DEVICE(u)->state);
-}
-
-static int device_add_escaped_name(Unit *u, const char *dn) {
- char *e;
- int r;
-
- assert(u);
- assert(dn);
- assert(dn[0] == '/');
-
- if (!(e = unit_name_from_path(dn, ".device")))
- return -ENOMEM;
-
- r = unit_add_name(u, e);
- free(e);
-
- if (r < 0 && r != -EEXIST)
- return r;
-
- return 0;
-}
-
-static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) {
- char *e;
- Unit *u;
-
- assert(m);
- assert(dn);
- assert(dn[0] == '/');
- assert(_u);
-
- if (!(e = unit_name_from_path(dn, ".device")))
- return -ENOMEM;
-
- u = manager_get_unit(m, e);
- free(e);
-
- if (u) {
- *_u = u;
- return 1;
- }
-
- return 0;
-}
-
-static int device_update_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
- const char *sysfs, *model;
- Unit *u = NULL;
- int r;
- bool delete;
-
- assert(m);
-
- if (!(sysfs = udev_device_get_syspath(dev)))
- return -ENOMEM;
-
- if ((r = device_find_escape_name(m, path, &u)) < 0)
- return r;
-
- if (u && DEVICE(u)->sysfs && !path_equal(DEVICE(u)->sysfs, sysfs))
- return -EEXIST;
-
- if (!u) {
- delete = true;
-
- u = unit_new(m, sizeof(Device));
- if (!u)
- return -ENOMEM;
-
- r = device_add_escaped_name(u, path);
- if (r < 0)
- goto fail;
-
- unit_add_to_load_queue(u);
- } else
- delete = false;
-
- /* If this was created via some dependency and has not
- * actually been seen yet ->sysfs will not be
- * initialized. Hence initialize it if necessary. */
-
- if (!DEVICE(u)->sysfs) {
- Device *first;
-
- if (!(DEVICE(u)->sysfs = strdup(sysfs))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!m->devices_by_sysfs)
- if (!(m->devices_by_sysfs = hashmap_new(string_hash_func, string_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- first = hashmap_get(m->devices_by_sysfs, sysfs);
- LIST_PREPEND(Device, same_sysfs, first, DEVICE(u));
-
- if ((r = hashmap_replace(m->devices_by_sysfs, DEVICE(u)->sysfs, first)) < 0)
- goto fail;
- }
-
- if ((model = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE")) ||
- (model = udev_device_get_property_value(dev, "ID_MODEL"))) {
- if ((r = unit_set_description(u, model)) < 0)
- goto fail;
- } else
- if ((r = unit_set_description(u, path)) < 0)
- goto fail;
-
- if (main) {
- /* The additional systemd udev properties we only
- * interpret for the main object */
- const char *wants, *alias;
-
- if ((alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"))) {
- if (!is_path(alias))
- log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias);
- else {
- if ((r = device_add_escaped_name(u, alias)) < 0)
- goto fail;
- }
- }
-
- if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) {
- char *state, *w;
- size_t l;
-
- FOREACH_WORD_QUOTED(w, l, wants, state) {
- char *e;
-
- if (!(e = strndup(w, l))) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
- free(e);
-
- if (r < 0)
- goto fail;
- }
- }
- }
-
- unit_add_to_dbus_queue(u);
- return 0;
-
-fail:
- log_warning("Failed to load device unit: %s", strerror(-r));
-
- if (delete && u)
- unit_free(u);
-
- return r;
-}
-
-static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
- const char *sysfs, *dn;
- struct udev_list_entry *item = NULL, *first = NULL;
-
- assert(m);
-
- if (!(sysfs = udev_device_get_syspath(dev)))
- return -ENOMEM;
-
- /* Add the main unit named after the sysfs path */
- device_update_unit(m, dev, sysfs, true);
-
- /* Add an additional unit for the device node */
- if ((dn = udev_device_get_devnode(dev)))
- device_update_unit(m, dev, dn, false);
-
- /* Add additional units for all symlinks */
- first = udev_device_get_devlinks_list_entry(dev);
- udev_list_entry_foreach(item, first) {
- const char *p;
- struct stat st;
-
- /* Don't bother with the /dev/block links */
- p = udev_list_entry_get_name(item);
-
- if (path_startswith(p, "/dev/block/") ||
- path_startswith(p, "/dev/char/"))
- continue;
-
- /* Verify that the symlink in the FS actually belongs
- * to this device. This is useful to deal with
- * conflicting devices, e.g. when two disks want the
- * same /dev/disk/by-label/xxx link because they have
- * the same label. We want to make sure that the same
- * device that won the symlink wins in systemd, so we
- * check the device node major/minor*/
- if (stat(p, &st) >= 0)
- if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
- st.st_rdev != udev_device_get_devnum(dev))
- continue;
-
- device_update_unit(m, dev, p, false);
- }
-
- if (update_state) {
- Device *d, *l;
-
- manager_dispatch_load_queue(m);
-
- l = hashmap_get(m->devices_by_sysfs, sysfs);
- LIST_FOREACH(same_sysfs, d, l)
- device_set_state(d, DEVICE_PLUGGED);
- }
-
- return 0;
-}
-
-static int device_process_path(Manager *m, const char *path, bool update_state) {
- int r;
- struct udev_device *dev;
-
- assert(m);
- assert(path);
-
- if (!(dev = udev_device_new_from_syspath(m->udev, path))) {
- log_warning("Failed to get udev device object from udev for path %s.", path);
- return -ENOMEM;
- }
-
- r = device_process_new_device(m, dev, update_state);
- udev_device_unref(dev);
- return r;
-}
-
-static int device_process_removed_device(Manager *m, struct udev_device *dev) {
- const char *sysfs;
- Device *d;
-
- assert(m);
- assert(dev);
-
- if (!(sysfs = udev_device_get_syspath(dev)))
- return -ENOMEM;
-
- /* Remove all units of this sysfs path */
- while ((d = hashmap_get(m->devices_by_sysfs, sysfs))) {
- device_unset_sysfs(d);
- device_set_state(d, DEVICE_DEAD);
- }
-
- return 0;
-}
-
-static Unit *device_following(Unit *u) {
- Device *d = DEVICE(u);
- Device *other, *first = NULL;
-
- assert(d);
-
- if (startswith(u->id, "sys-"))
- return NULL;
-
- /* Make everybody follow the unit that's named after the sysfs path */
- for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
- if (startswith(UNIT(other)->id, "sys-"))
- return UNIT(other);
-
- for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev) {
- if (startswith(UNIT(other)->id, "sys-"))
- return UNIT(other);
-
- first = other;
- }
-
- return UNIT(first);
-}
-
-static int device_following_set(Unit *u, Set **_s) {
- Device *d = DEVICE(u);
- Device *other;
- Set *s;
- int r;
-
- assert(d);
- assert(_s);
-
- if (!d->same_sysfs_prev && !d->same_sysfs_next) {
- *_s = NULL;
- return 0;
- }
-
- if (!(s = set_new(NULL, NULL)))
- return -ENOMEM;
-
- for (other = d->same_sysfs_next; other; other = other->same_sysfs_next)
- if ((r = set_put(s, other)) < 0)
- goto fail;
-
- for (other = d->same_sysfs_prev; other; other = other->same_sysfs_prev)
- if ((r = set_put(s, other)) < 0)
- goto fail;
-
- *_s = s;
- return 1;
-
-fail:
- set_free(s);
- return r;
-}
-
-static void device_shutdown(Manager *m) {
- assert(m);
-
- if (m->udev_monitor) {
- udev_monitor_unref(m->udev_monitor);
- m->udev_monitor = NULL;
- }
-
- if (m->udev) {
- udev_unref(m->udev);
- m->udev = NULL;
- }
-
- hashmap_free(m->devices_by_sysfs);
- m->devices_by_sysfs = NULL;
-}
-
-static int device_enumerate(Manager *m) {
- struct epoll_event ev;
- int r;
- struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
-
- assert(m);
-
- if (!m->udev) {
- if (!(m->udev = udev_new()))
- return -ENOMEM;
-
- if (!(m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* This will fail if we are unprivileged, but that
- * should not matter much, as user instances won't run
- * during boot. */
- udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
-
- if (udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd") < 0) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (udev_monitor_enable_receiving(m->udev_monitor) < 0) {
- r = -EIO;
- goto fail;
- }
-
- m->udev_watch.type = WATCH_UDEV;
- m->udev_watch.fd = udev_monitor_get_fd(m->udev_monitor);
-
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->udev_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_watch.fd, &ev) < 0)
- return -errno;
- }
-
- if (!(e = udev_enumerate_new(m->udev))) {
- r = -ENOMEM;
- goto fail;
- }
- if (udev_enumerate_add_match_tag(e, "systemd") < 0) {
- r = -EIO;
- goto fail;
- }
-
- if (udev_enumerate_scan_devices(e) < 0) {
- r = -EIO;
- goto fail;
- }
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first)
- device_process_path(m, udev_list_entry_get_name(item), false);
-
- udev_enumerate_unref(e);
- return 0;
-
-fail:
- if (e)
- udev_enumerate_unref(e);
-
- device_shutdown(m);
- return r;
-}
-
-void device_fd_event(Manager *m, int events) {
- struct udev_device *dev;
- int r;
- const char *action, *ready;
-
- assert(m);
-
- if (events != EPOLLIN) {
- static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5);
-
- if (!ratelimit_test(&limit))
- log_error("Failed to get udev event: %m");
- if (!(events & EPOLLIN))
- return;
- }
-
- if (!(dev = udev_monitor_receive_device(m->udev_monitor))) {
- /*
- * libudev might filter-out devices which pass the bloom filter,
- * so getting NULL here is not necessarily an error
- */
- return;
- }
-
- if (!(action = udev_device_get_action(dev))) {
- log_error("Failed to get udev action string.");
- goto fail;
- }
-
- ready = udev_device_get_property_value(dev, "SYSTEMD_READY");
-
- if (streq(action, "remove") || (ready && parse_boolean(ready) == 0)) {
- if ((r = device_process_removed_device(m, dev)) < 0) {
- log_error("Failed to process udev device event: %s", strerror(-r));
- goto fail;
- }
- } else {
- if ((r = device_process_new_device(m, dev, true)) < 0) {
- log_error("Failed to process udev device event: %s", strerror(-r));
- goto fail;
- }
- }
-
-fail:
- udev_device_unref(dev);
-}
-
-static const char* const device_state_table[_DEVICE_STATE_MAX] = {
- [DEVICE_DEAD] = "dead",
- [DEVICE_PLUGGED] = "plugged"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
-
-const UnitVTable device_vtable = {
- .suffix = ".device",
- .object_size = sizeof(Device),
- .sections =
- "Unit\0"
- "Device\0"
- "Install\0",
-
- .no_instances = true,
-
- .init = device_init,
-
- .load = unit_load_fragment_and_dropin_optional,
- .done = device_done,
- .coldplug = device_coldplug,
-
- .dump = device_dump,
-
- .active_state = device_active_state,
- .sub_state_to_string = device_sub_state_to_string,
-
- .bus_interface = "org.freedesktop.systemd1.Device",
- .bus_message_handler = bus_device_message_handler,
- .bus_invalidating_properties = bus_device_invalidating_properties,
-
- .following = device_following,
- .following_set = device_following_set,
-
- .enumerate = device_enumerate,
- .shutdown = device_shutdown
-};
diff --git a/src/device.h b/src/device.h
deleted file mode 100644
index a05c3d3..0000000
--- a/src/device.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foodevicehfoo
-#define foodevicehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Device Device;
-
-#include "unit.h"
-
-/* We simply watch devices, we cannot plug/unplug them. That
- * simplifies the state engine greatly */
-typedef enum DeviceState {
- DEVICE_DEAD,
- DEVICE_PLUGGED,
- _DEVICE_STATE_MAX,
- _DEVICE_STATE_INVALID = -1
-} DeviceState;
-
-struct Device {
- Unit meta;
-
- char *sysfs;
-
- /* In order to be able to distinguish dependencies on
- different device nodes we might end up creating multiple
- devices for the same sysfs path. We chain them up here. */
-
- LIST_FIELDS(struct Device, same_sysfs);
-
- DeviceState state;
-};
-
-extern const UnitVTable device_vtable;
-
-void device_fd_event(Manager *m, int events);
-
-const char* device_state_to_string(DeviceState i);
-DeviceState device_state_from_string(const char *s);
-
-#endif
diff --git a/src/execute.c b/src/execute.c
deleted file mode 100644
index 179ca7e..0000000
--- a/src/execute.c
+++ /dev/null
@@ -1,2113 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/prctl.h>
-#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/mount.h>
-#include <linux/fs.h>
-#include <linux/oom.h>
-
-#ifdef HAVE_PAM
-#include <security/pam_appl.h>
-#endif
-
-#include "execute.h"
-#include "strv.h"
-#include "macro.h"
-#include "capability.h"
-#include "util.h"
-#include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
-#include "cgroup.h"
-#include "namespace.h"
-#include "tcpwrap.h"
-#include "exit-status.h"
-#include "missing.h"
-#include "utmp-wtmp.h"
-#include "def.h"
-#include "loopback-setup.h"
-
-/* This assumes there is a 'tty' group */
-#define TTY_MODE 0620
-
-static int shift_fds(int fds[], unsigned n_fds) {
- int start, restart_from;
-
- if (n_fds <= 0)
- return 0;
-
- /* Modifies the fds array! (sorts it) */
-
- assert(fds);
-
- start = 0;
- for (;;) {
- int i;
-
- restart_from = -1;
-
- for (i = start; i < (int) n_fds; i++) {
- int nfd;
-
- /* Already at right index? */
- if (fds[i] == i+3)
- continue;
-
- if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
- return -errno;
-
- close_nointr_nofail(fds[i]);
- fds[i] = nfd;
-
- /* Hmm, the fd we wanted isn't free? Then
- * let's remember that and try again from here*/
- if (nfd != i+3 && restart_from < 0)
- restart_from = i;
- }
-
- if (restart_from < 0)
- break;
-
- start = restart_from;
- }
-
- return 0;
-}
-
-static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
- unsigned i;
- int r;
-
- if (n_fds <= 0)
- return 0;
-
- assert(fds);
-
- /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
-
- for (i = 0; i < n_fds; i++) {
-
- if ((r = fd_nonblock(fds[i], nonblock)) < 0)
- return r;
-
- /* We unconditionally drop FD_CLOEXEC from the fds,
- * since after all we want to pass these fds to our
- * children */
-
- if ((r = fd_cloexec(fds[i], false)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static const char *tty_path(const ExecContext *context) {
- assert(context);
-
- if (context->tty_path)
- return context->tty_path;
-
- return "/dev/console";
-}
-
-void exec_context_tty_reset(const ExecContext *context) {
- assert(context);
-
- if (context->tty_vhangup)
- terminal_vhangup(tty_path(context));
-
- if (context->tty_reset)
- reset_terminal(tty_path(context));
-
- if (context->tty_vt_disallocate && context->tty_path)
- vt_disallocate(context->tty_path);
-}
-
-static int open_null_as(int flags, int nfd) {
- int fd, r;
-
- assert(nfd >= 0);
-
- if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
- return -errno;
-
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- close_nointr_nofail(fd);
- } else
- r = nfd;
-
- return r;
-}
-
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, int nfd) {
- int fd, r;
- union sockaddr_union sa;
-
- assert(context);
- assert(output < _EXEC_OUTPUT_MAX);
- assert(ident);
- assert(nfd >= 0);
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0)
- return -errno;
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
-
- r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
- if (r < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- if (shutdown(fd, SHUT_RD) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- dprintf(fd,
- "%s\n"
- "%i\n"
- "%i\n"
- "%i\n"
- "%i\n"
- "%i\n",
- context->syslog_identifier ? context->syslog_identifier : ident,
- context->syslog_priority,
- !!context->syslog_level_prefix,
- output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
- output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
- output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
-
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- close_nointr_nofail(fd);
- } else
- r = nfd;
-
- return r;
-}
-static int open_terminal_as(const char *path, mode_t mode, int nfd) {
- int fd, r;
-
- assert(path);
- assert(nfd >= 0);
-
- if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
- return fd;
-
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- close_nointr_nofail(fd);
- } else
- r = nfd;
-
- return r;
-}
-
-static bool is_terminal_input(ExecInput i) {
- return
- i == EXEC_INPUT_TTY ||
- i == EXEC_INPUT_TTY_FORCE ||
- i == EXEC_INPUT_TTY_FAIL;
-}
-
-static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
-
- if (is_terminal_input(std_input) && !apply_tty_stdin)
- return EXEC_INPUT_NULL;
-
- if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
- return EXEC_INPUT_NULL;
-
- return std_input;
-}
-
-static int fixup_output(ExecOutput std_output, int socket_fd) {
-
- if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
- return EXEC_OUTPUT_INHERIT;
-
- return std_output;
-}
-
-static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
- ExecInput i;
-
- assert(context);
-
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
-
- switch (i) {
-
- case EXEC_INPUT_NULL:
- return open_null_as(O_RDONLY, STDIN_FILENO);
-
- case EXEC_INPUT_TTY:
- case EXEC_INPUT_TTY_FORCE:
- case EXEC_INPUT_TTY_FAIL: {
- int fd, r;
-
- if ((fd = acquire_terminal(
- tty_path(context),
- i == EXEC_INPUT_TTY_FAIL,
- i == EXEC_INPUT_TTY_FORCE,
- false)) < 0)
- return fd;
-
- if (fd != STDIN_FILENO) {
- r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
- close_nointr_nofail(fd);
- } else
- r = STDIN_FILENO;
-
- return r;
- }
-
- case EXEC_INPUT_SOCKET:
- return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
-
- default:
- assert_not_reached("Unknown input type");
- }
-}
-
-static int setup_output(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) {
- ExecOutput o;
- ExecInput i;
-
- assert(context);
- assert(ident);
-
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
- o = fixup_output(context->std_output, socket_fd);
-
- /* This expects the input is already set up */
-
- switch (o) {
-
- case EXEC_OUTPUT_INHERIT:
-
- /* If input got downgraded, inherit the original value */
- if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
-
- /* If the input is connected to anything that's not a /dev/null, inherit that... */
- if (i != EXEC_INPUT_NULL)
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
- /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
- if (getppid() != 1)
- return STDOUT_FILENO;
-
- /* We need to open /dev/null here anew, to get the
- * right access mode. So we fall through */
-
- case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_TTY:
- if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
- /* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
- case EXEC_OUTPUT_KMSG:
- case EXEC_OUTPUT_KMSG_AND_CONSOLE:
- case EXEC_OUTPUT_JOURNAL:
- case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, o, ident, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SOCKET:
- assert(socket_fd >= 0);
- return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
- default:
- assert_not_reached("Unknown output type");
- }
-}
-
-static int setup_error(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) {
- ExecOutput o, e;
- ExecInput i;
-
- assert(context);
- assert(ident);
-
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
- o = fixup_output(context->std_output, socket_fd);
- e = fixup_output(context->std_error, socket_fd);
-
- /* This expects the input and output are already set up */
-
- /* Don't change the stderr file descriptor if we inherit all
- * the way and are not on a tty */
- if (e == EXEC_OUTPUT_INHERIT &&
- o == EXEC_OUTPUT_INHERIT &&
- i == EXEC_INPUT_NULL &&
- !is_terminal_input(context->std_input) &&
- getppid () != 1)
- return STDERR_FILENO;
-
- /* Duplicate from stdout if possible */
- if (e == o || e == EXEC_OUTPUT_INHERIT)
- return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
- switch (e) {
-
- case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDERR_FILENO);
-
- case EXEC_OUTPUT_TTY:
- if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
- /* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
-
- case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
- case EXEC_OUTPUT_KMSG:
- case EXEC_OUTPUT_KMSG_AND_CONSOLE:
- case EXEC_OUTPUT_JOURNAL:
- case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, e, ident, STDERR_FILENO);
-
- case EXEC_OUTPUT_SOCKET:
- assert(socket_fd >= 0);
- return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
- default:
- assert_not_reached("Unknown error type");
- }
-}
-
-static int chown_terminal(int fd, uid_t uid) {
- struct stat st;
-
- assert(fd >= 0);
-
- /* This might fail. What matters are the results. */
- (void) fchown(fd, uid, -1);
- (void) fchmod(fd, TTY_MODE);
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
- return -EPERM;
-
- return 0;
-}
-
-static int setup_confirm_stdio(const ExecContext *context,
- int *_saved_stdin,
- int *_saved_stdout) {
- int fd = -1, saved_stdin, saved_stdout = -1, r;
-
- assert(context);
- assert(_saved_stdin);
- assert(_saved_stdout);
-
- /* This returns positive EXIT_xxx return values instead of
- * negative errno style values! */
-
- if ((saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3)) < 0)
- return EXIT_STDIN;
-
- if ((saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3)) < 0) {
- r = EXIT_STDOUT;
- goto fail;
- }
-
- if ((fd = acquire_terminal(
- tty_path(context),
- context->std_input == EXEC_INPUT_TTY_FAIL,
- context->std_input == EXEC_INPUT_TTY_FORCE,
- false)) < 0) {
- r = EXIT_STDIN;
- goto fail;
- }
-
- if (chown_terminal(fd, getuid()) < 0) {
- r = EXIT_STDIN;
- goto fail;
- }
-
- if (dup2(fd, STDIN_FILENO) < 0) {
- r = EXIT_STDIN;
- goto fail;
- }
-
- if (dup2(fd, STDOUT_FILENO) < 0) {
- r = EXIT_STDOUT;
- goto fail;
- }
-
- if (fd >= 2)
- close_nointr_nofail(fd);
-
- *_saved_stdin = saved_stdin;
- *_saved_stdout = saved_stdout;
-
- return 0;
-
-fail:
- if (saved_stdout >= 0)
- close_nointr_nofail(saved_stdout);
-
- if (saved_stdin >= 0)
- close_nointr_nofail(saved_stdin);
-
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int restore_confirm_stdio(const ExecContext *context,
- int *saved_stdin,
- int *saved_stdout,
- bool *keep_stdin,
- bool *keep_stdout) {
-
- assert(context);
- assert(saved_stdin);
- assert(*saved_stdin >= 0);
- assert(saved_stdout);
- assert(*saved_stdout >= 0);
-
- /* This returns positive EXIT_xxx return values instead of
- * negative errno style values! */
-
- if (is_terminal_input(context->std_input)) {
-
- /* The service wants terminal input. */
-
- *keep_stdin = true;
- *keep_stdout =
- context->std_output == EXEC_OUTPUT_INHERIT ||
- context->std_output == EXEC_OUTPUT_TTY;
-
- } else {
- /* If the service doesn't want a controlling terminal,
- * then we need to get rid entirely of what we have
- * already. */
-
- if (release_terminal() < 0)
- return EXIT_STDIN;
-
- if (dup2(*saved_stdin, STDIN_FILENO) < 0)
- return EXIT_STDIN;
-
- if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
- return EXIT_STDOUT;
-
- *keep_stdout = *keep_stdin = false;
- }
-
- return 0;
-}
-
-static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
- bool keep_groups = false;
- int r;
-
- assert(context);
-
- /* Lookup and set GID and supplementary group list. Here too
- * we avoid NSS lookups for gid=0. */
-
- if (context->group || username) {
-
- if (context->group) {
- const char *g = context->group;
-
- if ((r = get_group_creds(&g, &gid)) < 0)
- return r;
- }
-
- /* First step, initialize groups from /etc/groups */
- if (username && gid != 0) {
- if (initgroups(username, gid) < 0)
- return -errno;
-
- keep_groups = true;
- }
-
- /* Second step, set our gids */
- if (setresgid(gid, gid, gid) < 0)
- return -errno;
- }
-
- if (context->supplementary_groups) {
- int ngroups_max, k;
- gid_t *gids;
- char **i;
-
- /* Final step, initialize any manually set supplementary groups */
- assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
-
- if (!(gids = new(gid_t, ngroups_max)))
- return -ENOMEM;
-
- if (keep_groups) {
- if ((k = getgroups(ngroups_max, gids)) < 0) {
- free(gids);
- return -errno;
- }
- } else
- k = 0;
-
- STRV_FOREACH(i, context->supplementary_groups) {
- const char *g;
-
- if (k >= ngroups_max) {
- free(gids);
- return -E2BIG;
- }
-
- g = *i;
- r = get_group_creds(&g, gids+k);
- if (r < 0) {
- free(gids);
- return r;
- }
-
- k++;
- }
-
- if (setgroups(k, gids) < 0) {
- free(gids);
- return -errno;
- }
-
- free(gids);
- }
-
- return 0;
-}
-
-static int enforce_user(const ExecContext *context, uid_t uid) {
- int r;
- assert(context);
-
- /* Sets (but doesn't lookup) the uid and make sure we keep the
- * capabilities while doing so. */
-
- if (context->capabilities) {
- cap_t d;
- static const cap_value_t bits[] = {
- CAP_SETUID, /* Necessary so that we can run setresuid() below */
- CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
- };
-
- /* First step: If we need to keep capabilities but
- * drop privileges we need to make sure we keep our
- * caps, whiel we drop privileges. */
- if (uid != 0) {
- int sb = context->secure_bits|SECURE_KEEP_CAPS;
-
- if (prctl(PR_GET_SECUREBITS) != sb)
- if (prctl(PR_SET_SECUREBITS, sb) < 0)
- return -errno;
- }
-
- /* Second step: set the capabilities. This will reduce
- * the capabilities to the minimum we need. */
-
- if (!(d = cap_dup(context->capabilities)))
- return -errno;
-
- if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
- r = -errno;
- cap_free(d);
- return r;
- }
-
- if (cap_set_proc(d) < 0) {
- r = -errno;
- cap_free(d);
- return r;
- }
-
- cap_free(d);
- }
-
- /* Third step: actually set the uids */
- if (setresuid(uid, uid, uid) < 0)
- return -errno;
-
- /* At this point we should have all necessary capabilities but
- are otherwise a normal user. However, the caps might got
- corrupted due to the setresuid() so we need clean them up
- later. This is done outside of this call. */
-
- return 0;
-}
-
-#ifdef HAVE_PAM
-
-static int null_conv(
- int num_msg,
- const struct pam_message **msg,
- struct pam_response **resp,
- void *appdata_ptr) {
-
- /* We don't support conversations */
-
- return PAM_CONV_ERR;
-}
-
-static int setup_pam(
- const char *name,
- const char *user,
- const char *tty,
- char ***pam_env,
- int fds[], unsigned n_fds) {
-
- static const struct pam_conv conv = {
- .conv = null_conv,
- .appdata_ptr = NULL
- };
-
- pam_handle_t *handle = NULL;
- sigset_t ss, old_ss;
- int pam_code = PAM_SUCCESS;
- int err;
- char **e = NULL;
- bool close_session = false;
- pid_t pam_pid = 0, parent_pid;
-
- assert(name);
- assert(user);
- assert(pam_env);
-
- /* We set up PAM in the parent process, then fork. The child
- * will then stay around until killed via PR_GET_PDEATHSIG or
- * systemd via the cgroup logic. It will then remove the PAM
- * session again. The parent process will exec() the actual
- * daemon. We do things this way to ensure that the main PID
- * of the daemon is the one we initially fork()ed. */
-
- if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
- handle = NULL;
- goto fail;
- }
-
- if (tty)
- if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
- goto fail;
-
- if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
- goto fail;
-
- if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
- goto fail;
-
- close_session = true;
-
- if ((!(e = pam_getenvlist(handle)))) {
- pam_code = PAM_BUF_ERR;
- goto fail;
- }
-
- /* Block SIGTERM, so that we know that it won't get lost in
- * the child */
- if (sigemptyset(&ss) < 0 ||
- sigaddset(&ss, SIGTERM) < 0 ||
- sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
- goto fail;
-
- parent_pid = getpid();
-
- if ((pam_pid = fork()) < 0)
- goto fail;
-
- if (pam_pid == 0) {
- int sig;
- int r = EXIT_PAM;
-
- /* The child's job is to reset the PAM session on
- * termination */
-
- /* This string must fit in 10 chars (i.e. the length
- * of "/sbin/init"), to look pretty in /bin/ps */
- rename_process("(sd-pam)");
-
- /* Make sure we don't keep open the passed fds in this
- child. We assume that otherwise only those fds are
- open here that have been opened by PAM. */
- close_many(fds, n_fds);
-
- /* Wait until our parent died. This will most likely
- * not work since the kernel does not allow
- * unprivileged parents kill their privileged children
- * this way. We rely on the control groups kill logic
- * to do the rest for us. */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- goto child_finish;
-
- /* Check if our parent process might already have
- * died? */
- if (getppid() == parent_pid) {
- for (;;) {
- if (sigwait(&ss, &sig) < 0) {
- if (errno == EINTR)
- continue;
-
- goto child_finish;
- }
-
- assert(sig == SIGTERM);
- break;
- }
- }
-
- /* If our parent died we'll end the session */
- if (getppid() != parent_pid)
- if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
- goto child_finish;
-
- r = 0;
-
- child_finish:
- pam_end(handle, pam_code | PAM_DATA_SILENT);
- _exit(r);
- }
-
- /* If the child was forked off successfully it will do all the
- * cleanups, so forget about the handle here. */
- handle = NULL;
-
- /* Unblock SIGTERM again in the parent */
- if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
- goto fail;
-
- /* We close the log explicitly here, since the PAM modules
- * might have opened it, but we don't want this fd around. */
- closelog();
-
- *pam_env = e;
- e = NULL;
-
- return 0;
-
-fail:
- if (pam_code != PAM_SUCCESS)
- err = -EPERM; /* PAM errors do not map to errno */
- else
- err = -errno;
-
- if (handle) {
- if (close_session)
- pam_code = pam_close_session(handle, PAM_DATA_SILENT);
-
- pam_end(handle, pam_code | PAM_DATA_SILENT);
- }
-
- strv_free(e);
-
- closelog();
-
- if (pam_pid > 1) {
- kill(pam_pid, SIGTERM);
- kill(pam_pid, SIGCONT);
- }
-
- return err;
-}
-#endif
-
-static int do_capability_bounding_set_drop(uint64_t drop) {
- unsigned long i;
- cap_t old_cap = NULL, new_cap = NULL;
- cap_flag_value_t fv;
- int r;
-
- /* If we are run as PID 1 we will lack CAP_SETPCAP by default
- * in the effective set (yes, the kernel drops that when
- * executing init!), so get it back temporarily so that we can
- * call PR_CAPBSET_DROP. */
-
- old_cap = cap_get_proc();
- if (!old_cap)
- return -errno;
-
- if (cap_get_flag(old_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (fv != CAP_SET) {
- static const cap_value_t v = CAP_SETPCAP;
-
- new_cap = cap_dup(old_cap);
- if (!new_cap) {
- r = -errno;
- goto finish;
- }
-
- if (cap_set_flag(new_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (cap_set_proc(new_cap) < 0) {
- r = -errno;
- goto finish;
- }
- }
-
- for (i = 0; i <= cap_last_cap(); i++)
- if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
- if (prctl(PR_CAPBSET_DROP, i) < 0) {
- r = -errno;
- goto finish;
- }
- }
-
- r = 0;
-
-finish:
- if (new_cap)
- cap_free(new_cap);
-
- if (old_cap) {
- cap_set_proc(old_cap);
- cap_free(old_cap);
- }
-
- return r;
-}
-
-static void rename_process_from_path(const char *path) {
- char process_name[11];
- const char *p;
- size_t l;
-
- /* This resulting string must fit in 10 chars (i.e. the length
- * of "/sbin/init") to look pretty in /bin/ps */
-
- p = file_name_from_path(path);
- if (isempty(p)) {
- rename_process("(...)");
- return;
- }
-
- l = strlen(p);
- if (l > 8) {
- /* The end of the process name is usually more
- * interesting, since the first bit might just be
- * "systemd-" */
- p = p + l - 8;
- l = 8;
- }
-
- process_name[0] = '(';
- memcpy(process_name+1, p, l);
- process_name[1+l] = ')';
- process_name[1+l+1] = 0;
-
- rename_process(process_name);
-}
-
-int exec_spawn(ExecCommand *command,
- char **argv,
- const ExecContext *context,
- int fds[], unsigned n_fds,
- char **environment,
- bool apply_permissions,
- bool apply_chroot,
- bool apply_tty_stdin,
- bool confirm_spawn,
- CGroupBonding *cgroup_bondings,
- CGroupAttribute *cgroup_attributes,
- pid_t *ret) {
-
- pid_t pid;
- int r;
- char *line;
- int socket_fd;
- char **files_env = NULL;
-
- assert(command);
- assert(context);
- assert(ret);
- assert(fds || n_fds <= 0);
-
- if (context->std_input == EXEC_INPUT_SOCKET ||
- context->std_output == EXEC_OUTPUT_SOCKET ||
- context->std_error == EXEC_OUTPUT_SOCKET) {
-
- if (n_fds != 1)
- return -EINVAL;
-
- socket_fd = fds[0];
-
- fds = NULL;
- n_fds = 0;
- } else
- socket_fd = -1;
-
- if ((r = exec_context_load_environment(context, &files_env)) < 0) {
- log_error("Failed to load environment files: %s", strerror(-r));
- return r;
- }
-
- if (!argv)
- argv = command->argv;
-
- if (!(line = exec_command_line(argv))) {
- r = -ENOMEM;
- goto fail_parent;
- }
-
- log_debug("About to execute: %s", line);
- free(line);
-
- r = cgroup_bonding_realize_list(cgroup_bondings);
- if (r < 0)
- goto fail_parent;
-
- cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
-
- if ((pid = fork()) < 0) {
- r = -errno;
- goto fail_parent;
- }
-
- if (pid == 0) {
- int i, err;
- sigset_t ss;
- const char *username = NULL, *home = NULL;
- uid_t uid = (uid_t) -1;
- gid_t gid = (gid_t) -1;
- char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
- unsigned n_env = 0;
- int saved_stdout = -1, saved_stdin = -1;
- bool keep_stdout = false, keep_stdin = false, set_access = false;
-
- /* child */
-
- rename_process_from_path(command->path);
-
- /* We reset exactly these signals, since they are the
- * only ones we set to SIG_IGN in the main daemon. All
- * others we leave untouched because we set them to
- * SIG_DFL or a valid handler initially, both of which
- * will be demoted to SIG_DFL. */
- default_signals(SIGNALS_CRASH_HANDLER,
- SIGNALS_IGNORE, -1);
-
- if (context->ignore_sigpipe)
- ignore_signals(SIGPIPE, -1);
-
- assert_se(sigemptyset(&ss) == 0);
- if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
- err = -errno;
- r = EXIT_SIGNAL_MASK;
- goto fail_child;
- }
-
- /* Close sockets very early to make sure we don't
- * block init reexecution because it cannot bind its
- * sockets */
- log_forget_fds();
- err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds,
- socket_fd >= 0 ? 1 : n_fds);
- if (err < 0) {
- r = EXIT_FDS;
- goto fail_child;
- }
-
- if (!context->same_pgrp)
- if (setsid() < 0) {
- err = -errno;
- r = EXIT_SETSID;
- goto fail_child;
- }
-
- if (context->tcpwrap_name) {
- if (socket_fd >= 0)
- if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) {
- err = -EACCES;
- r = EXIT_TCPWRAP;
- goto fail_child;
- }
-
- for (i = 0; i < (int) n_fds; i++) {
- if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) {
- err = -EACCES;
- r = EXIT_TCPWRAP;
- goto fail_child;
- }
- }
- }
-
- exec_context_tty_reset(context);
-
- /* We skip the confirmation step if we shall not apply the TTY */
- if (confirm_spawn &&
- (!is_terminal_input(context->std_input) || apply_tty_stdin)) {
- char response;
-
- /* Set up terminal for the question */
- if ((r = setup_confirm_stdio(context,
- &saved_stdin, &saved_stdout))) {
- err = -errno;
- goto fail_child;
- }
-
- /* Now ask the question. */
- if (!(line = exec_command_line(argv))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- r = ask(&response, "yns", "Execute %s? [Yes, No, Skip] ", line);
- free(line);
-
- if (r < 0 || response == 'n') {
- err = -ECANCELED;
- r = EXIT_CONFIRM;
- goto fail_child;
- } else if (response == 's') {
- err = r = 0;
- goto fail_child;
- }
-
- /* Release terminal for the question */
- if ((r = restore_confirm_stdio(context,
- &saved_stdin, &saved_stdout,
- &keep_stdin, &keep_stdout))) {
- err = -errno;
- goto fail_child;
- }
- }
-
- /* If a socket is connected to STDIN/STDOUT/STDERR, we
- * must sure to drop O_NONBLOCK */
- if (socket_fd >= 0)
- fd_nonblock(socket_fd, false);
-
- if (!keep_stdin) {
- err = setup_input(context, socket_fd, apply_tty_stdin);
- if (err < 0) {
- r = EXIT_STDIN;
- goto fail_child;
- }
- }
-
- if (!keep_stdout) {
- err = setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin);
- if (err < 0) {
- r = EXIT_STDOUT;
- goto fail_child;
- }
- }
-
- err = setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin);
- if (err < 0) {
- r = EXIT_STDERR;
- goto fail_child;
- }
-
- if (cgroup_bondings) {
- err = cgroup_bonding_install_list(cgroup_bondings, 0);
- if (err < 0) {
- r = EXIT_CGROUP;
- goto fail_child;
- }
- }
-
- if (context->oom_score_adjust_set) {
- char t[16];
-
- snprintf(t, sizeof(t), "%i", context->oom_score_adjust);
- char_array_0(t);
-
- if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) {
- /* Compatibility with Linux <= 2.6.35 */
-
- int adj;
-
- adj = (context->oom_score_adjust * -OOM_DISABLE) / OOM_SCORE_ADJ_MAX;
- adj = CLAMP(adj, OOM_DISABLE, OOM_ADJUST_MAX);
-
- snprintf(t, sizeof(t), "%i", adj);
- char_array_0(t);
-
- if (write_one_line_file("/proc/self/oom_adj", t) < 0
- && errno != EACCES) {
- err = -errno;
- r = EXIT_OOM_ADJUST;
- goto fail_child;
- }
- }
- }
-
- if (context->nice_set)
- if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
- err = -errno;
- r = EXIT_NICE;
- goto fail_child;
- }
-
- if (context->cpu_sched_set) {
- struct sched_param param;
-
- zero(param);
- param.sched_priority = context->cpu_sched_priority;
-
- if (sched_setscheduler(0, context->cpu_sched_policy |
- (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), ¶m) < 0) {
- err = -errno;
- r = EXIT_SETSCHEDULER;
- goto fail_child;
- }
- }
-
- if (context->cpuset)
- if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) {
- err = -errno;
- r = EXIT_CPUAFFINITY;
- goto fail_child;
- }
-
- if (context->ioprio_set)
- if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
- err = -errno;
- r = EXIT_IOPRIO;
- goto fail_child;
- }
-
- if (context->timer_slack_nsec_set)
- if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) {
- err = -errno;
- r = EXIT_TIMERSLACK;
- goto fail_child;
- }
-
- if (context->utmp_id)
- utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
-
- if (context->user) {
- username = context->user;
- err = get_user_creds(&username, &uid, &gid, &home);
- if (err < 0) {
- r = EXIT_USER;
- goto fail_child;
- }
-
- if (is_terminal_input(context->std_input)) {
- err = chown_terminal(STDIN_FILENO, uid);
- if (err < 0) {
- r = EXIT_STDIN;
- goto fail_child;
- }
- }
-
- if (cgroup_bondings && context->control_group_modify) {
- err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
- if (err >= 0)
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent);
- if (err < 0) {
- r = EXIT_CGROUP;
- goto fail_child;
- }
-
- set_access = true;
- }
- }
-
- if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) {
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent);
- if (err < 0) {
- r = EXIT_CGROUP;
- goto fail_child;
- }
- }
-
- if (apply_permissions) {
- err = enforce_groups(context, username, gid);
- if (err < 0) {
- r = EXIT_GROUP;
- goto fail_child;
- }
- }
-
- umask(context->umask);
-
-#ifdef HAVE_PAM
- if (context->pam_name && username) {
- err = setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds);
- if (err < 0) {
- r = EXIT_PAM;
- goto fail_child;
- }
- }
-#endif
- if (context->private_network) {
- if (unshare(CLONE_NEWNET) < 0) {
- err = -errno;
- r = EXIT_NETWORK;
- goto fail_child;
- }
-
- loopback_setup();
- }
-
- if (strv_length(context->read_write_dirs) > 0 ||
- strv_length(context->read_only_dirs) > 0 ||
- strv_length(context->inaccessible_dirs) > 0 ||
- context->mount_flags != MS_SHARED ||
- context->private_tmp) {
- err = setup_namespace(context->read_write_dirs,
- context->read_only_dirs,
- context->inaccessible_dirs,
- context->private_tmp,
- context->mount_flags);
- if (err < 0) {
- r = EXIT_NAMESPACE;
- goto fail_child;
- }
- }
-
- if (apply_chroot) {
- if (context->root_directory)
- if (chroot(context->root_directory) < 0) {
- err = -errno;
- r = EXIT_CHROOT;
- goto fail_child;
- }
-
- if (chdir(context->working_directory ? context->working_directory : "/") < 0) {
- err = -errno;
- r = EXIT_CHDIR;
- goto fail_child;
- }
- } else {
-
- char *d;
-
- if (asprintf(&d, "%s/%s",
- context->root_directory ? context->root_directory : "",
- context->working_directory ? context->working_directory : "") < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (chdir(d) < 0) {
- err = -errno;
- free(d);
- r = EXIT_CHDIR;
- goto fail_child;
- }
-
- free(d);
- }
-
- /* We repeat the fd closing here, to make sure that
- * nothing is leaked from the PAM modules */
- err = close_all_fds(fds, n_fds);
- if (err >= 0)
- err = shift_fds(fds, n_fds);
- if (err >= 0)
- err = flags_fds(fds, n_fds, context->non_blocking);
- if (err < 0) {
- r = EXIT_FDS;
- goto fail_child;
- }
-
- if (apply_permissions) {
-
- for (i = 0; i < RLIMIT_NLIMITS; i++) {
- if (!context->rlimit[i])
- continue;
-
- if (setrlimit(i, context->rlimit[i]) < 0) {
- err = -errno;
- r = EXIT_LIMITS;
- goto fail_child;
- }
- }
-
- if (context->capability_bounding_set_drop) {
- err = do_capability_bounding_set_drop(context->capability_bounding_set_drop);
- if (err < 0) {
- r = EXIT_CAPABILITIES;
- goto fail_child;
- }
- }
-
- if (context->user) {
- err = enforce_user(context, uid);
- if (err < 0) {
- r = EXIT_USER;
- goto fail_child;
- }
- }
-
- /* PR_GET_SECUREBITS is not privileged, while
- * PR_SET_SECUREBITS is. So to suppress
- * potential EPERMs we'll try not to call
- * PR_SET_SECUREBITS unless necessary. */
- if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
- if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
- err = -errno;
- r = EXIT_SECUREBITS;
- goto fail_child;
- }
-
- if (context->capabilities)
- if (cap_set_proc(context->capabilities) < 0) {
- err = -errno;
- r = EXIT_CAPABILITIES;
- goto fail_child;
- }
- }
-
- if (!(our_env = new0(char*, 7))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (n_fds > 0)
- if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 ||
- asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (home)
- if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (username)
- if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 ||
- asprintf(our_env + n_env++, "USER=%s", username) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (is_terminal_input(context->std_input) ||
- context->std_output == EXEC_OUTPUT_TTY ||
- context->std_error == EXEC_OUTPUT_TTY)
- if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- assert(n_env <= 7);
-
- if (!(final_env = strv_env_merge(
- 5,
- environment,
- our_env,
- context->environment,
- files_env,
- pam_env,
- NULL))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (!(final_argv = replace_env_argv(argv, final_env))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- final_env = strv_env_clean(final_env);
-
- execve(command->path, final_argv, final_env);
- err = -errno;
- r = EXIT_EXEC;
-
- fail_child:
- if (r != 0) {
- log_open();
- log_warning("Failed at step %s spawning %s: %s",
- exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
- command->path, strerror(-err));
- }
-
- strv_free(our_env);
- strv_free(final_env);
- strv_free(pam_env);
- strv_free(files_env);
- strv_free(final_argv);
-
- if (saved_stdin >= 0)
- close_nointr_nofail(saved_stdin);
-
- if (saved_stdout >= 0)
- close_nointr_nofail(saved_stdout);
-
- _exit(r);
- }
-
- strv_free(files_env);
-
- /* We add the new process to the cgroup both in the child (so
- * that we can be sure that no user code is ever executed
- * outside of the cgroup) and in the parent (so that we can be
- * sure that when we kill the cgroup the process will be
- * killed too). */
- if (cgroup_bondings)
- cgroup_bonding_install_list(cgroup_bondings, pid);
-
- log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
-
- exec_status_start(&command->exec_status, pid);
-
- *ret = pid;
- return 0;
-
-fail_parent:
- strv_free(files_env);
-
- return r;
-}
-
-void exec_context_init(ExecContext *c) {
- assert(c);
-
- c->umask = 0022;
- c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0);
- c->cpu_sched_policy = SCHED_OTHER;
- c->syslog_priority = LOG_DAEMON|LOG_INFO;
- c->syslog_level_prefix = true;
- c->mount_flags = MS_SHARED;
- c->kill_signal = SIGTERM;
- c->send_sigkill = true;
- c->control_group_persistent = -1;
- c->ignore_sigpipe = true;
-}
-
-void exec_context_done(ExecContext *c) {
- unsigned l;
-
- assert(c);
-
- strv_free(c->environment);
- c->environment = NULL;
-
- strv_free(c->environment_files);
- c->environment_files = NULL;
-
- for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
- free(c->rlimit[l]);
- c->rlimit[l] = NULL;
- }
-
- free(c->working_directory);
- c->working_directory = NULL;
- free(c->root_directory);
- c->root_directory = NULL;
-
- free(c->tty_path);
- c->tty_path = NULL;
-
- free(c->tcpwrap_name);
- c->tcpwrap_name = NULL;
-
- free(c->syslog_identifier);
- c->syslog_identifier = NULL;
-
- free(c->user);
- c->user = NULL;
-
- free(c->group);
- c->group = NULL;
-
- strv_free(c->supplementary_groups);
- c->supplementary_groups = NULL;
-
- free(c->pam_name);
- c->pam_name = NULL;
-
- if (c->capabilities) {
- cap_free(c->capabilities);
- c->capabilities = NULL;
- }
-
- strv_free(c->read_only_dirs);
- c->read_only_dirs = NULL;
-
- strv_free(c->read_write_dirs);
- c->read_write_dirs = NULL;
-
- strv_free(c->inaccessible_dirs);
- c->inaccessible_dirs = NULL;
-
- if (c->cpuset)
- CPU_FREE(c->cpuset);
-
- free(c->utmp_id);
- c->utmp_id = NULL;
-}
-
-void exec_command_done(ExecCommand *c) {
- assert(c);
-
- free(c->path);
- c->path = NULL;
-
- strv_free(c->argv);
- c->argv = NULL;
-}
-
-void exec_command_done_array(ExecCommand *c, unsigned n) {
- unsigned i;
-
- for (i = 0; i < n; i++)
- exec_command_done(c+i);
-}
-
-void exec_command_free_list(ExecCommand *c) {
- ExecCommand *i;
-
- while ((i = c)) {
- LIST_REMOVE(ExecCommand, command, c, i);
- exec_command_done(i);
- free(i);
- }
-}
-
-void exec_command_free_array(ExecCommand **c, unsigned n) {
- unsigned i;
-
- for (i = 0; i < n; i++) {
- exec_command_free_list(c[i]);
- c[i] = NULL;
- }
-}
-
-int exec_context_load_environment(const ExecContext *c, char ***l) {
- char **i, **r = NULL;
-
- assert(c);
- assert(l);
-
- STRV_FOREACH(i, c->environment_files) {
- char *fn;
- int k;
- bool ignore = false;
- char **p;
-
- fn = *i;
-
- if (fn[0] == '-') {
- ignore = true;
- fn ++;
- }
-
- if (!path_is_absolute(fn)) {
-
- if (ignore)
- continue;
-
- strv_free(r);
- return -EINVAL;
- }
-
- if ((k = load_env_file(fn, &p)) < 0) {
-
- if (ignore)
- continue;
-
- strv_free(r);
- return k;
- }
-
- if (r == NULL)
- r = p;
- else {
- char **m;
-
- m = strv_env_merge(2, r, p);
- strv_free(r);
- strv_free(p);
-
- if (!m)
- return -ENOMEM;
-
- r = m;
- }
- }
-
- *l = r;
-
- return 0;
-}
-
-static void strv_fprintf(FILE *f, char **l) {
- char **g;
-
- assert(f);
-
- STRV_FOREACH(g, l)
- fprintf(f, " %s", *g);
-}
-
-void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
- char ** e;
- unsigned i;
-
- assert(c);
- assert(f);
-
- if (!prefix)
- prefix = "";
-
- fprintf(f,
- "%sUMask: %04o\n"
- "%sWorkingDirectory: %s\n"
- "%sRootDirectory: %s\n"
- "%sNonBlocking: %s\n"
- "%sPrivateTmp: %s\n"
- "%sControlGroupModify: %s\n"
- "%sControlGroupPersistent: %s\n"
- "%sPrivateNetwork: %s\n",
- prefix, c->umask,
- prefix, c->working_directory ? c->working_directory : "/",
- prefix, c->root_directory ? c->root_directory : "/",
- prefix, yes_no(c->non_blocking),
- prefix, yes_no(c->private_tmp),
- prefix, yes_no(c->control_group_modify),
- prefix, yes_no(c->control_group_persistent),
- prefix, yes_no(c->private_network));
-
- STRV_FOREACH(e, c->environment)
- fprintf(f, "%sEnvironment: %s\n", prefix, *e);
-
- STRV_FOREACH(e, c->environment_files)
- fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
-
- if (c->tcpwrap_name)
- fprintf(f,
- "%sTCPWrapName: %s\n",
- prefix, c->tcpwrap_name);
-
- if (c->nice_set)
- fprintf(f,
- "%sNice: %i\n",
- prefix, c->nice);
-
- if (c->oom_score_adjust_set)
- fprintf(f,
- "%sOOMScoreAdjust: %i\n",
- prefix, c->oom_score_adjust);
-
- for (i = 0; i < RLIM_NLIMITS; i++)
- if (c->rlimit[i])
- fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
-
- if (c->ioprio_set)
- fprintf(f,
- "%sIOSchedulingClass: %s\n"
- "%sIOPriority: %i\n",
- prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
- prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
-
- if (c->cpu_sched_set)
- fprintf(f,
- "%sCPUSchedulingPolicy: %s\n"
- "%sCPUSchedulingPriority: %i\n"
- "%sCPUSchedulingResetOnFork: %s\n",
- prefix, sched_policy_to_string(c->cpu_sched_policy),
- prefix, c->cpu_sched_priority,
- prefix, yes_no(c->cpu_sched_reset_on_fork));
-
- if (c->cpuset) {
- fprintf(f, "%sCPUAffinity:", prefix);
- for (i = 0; i < c->cpuset_ncpus; i++)
- if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
- fprintf(f, " %i", i);
- fputs("\n", f);
- }
-
- if (c->timer_slack_nsec_set)
- fprintf(f, "%sTimerSlackNSec: %lu\n", prefix, c->timer_slack_nsec);
-
- fprintf(f,
- "%sStandardInput: %s\n"
- "%sStandardOutput: %s\n"
- "%sStandardError: %s\n",
- prefix, exec_input_to_string(c->std_input),
- prefix, exec_output_to_string(c->std_output),
- prefix, exec_output_to_string(c->std_error));
-
- if (c->tty_path)
- fprintf(f,
- "%sTTYPath: %s\n"
- "%sTTYReset: %s\n"
- "%sTTYVHangup: %s\n"
- "%sTTYVTDisallocate: %s\n",
- prefix, c->tty_path,
- prefix, yes_no(c->tty_reset),
- prefix, yes_no(c->tty_vhangup),
- prefix, yes_no(c->tty_vt_disallocate));
-
- if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL ||
- c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
- c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL ||
- c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE)
- fprintf(f,
- "%sSyslogFacility: %s\n"
- "%sSyslogLevel: %s\n",
- prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3),
- prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
-
- if (c->capabilities) {
- char *t;
- if ((t = cap_to_text(c->capabilities, NULL))) {
- fprintf(f, "%sCapabilities: %s\n",
- prefix, t);
- cap_free(t);
- }
- }
-
- if (c->secure_bits)
- fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
- prefix,
- (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
- (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
- (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
- (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
-
- if (c->capability_bounding_set_drop) {
- unsigned long l;
- fprintf(f, "%sCapabilityBoundingSet:", prefix);
-
- for (l = 0; l <= cap_last_cap(); l++)
- if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) {
- char *t;
-
- if ((t = cap_to_name(l))) {
- fprintf(f, " %s", t);
- cap_free(t);
- }
- }
-
- fputs("\n", f);
- }
-
- if (c->user)
- fprintf(f, "%sUser: %s\n", prefix, c->user);
- if (c->group)
- fprintf(f, "%sGroup: %s\n", prefix, c->group);
-
- if (strv_length(c->supplementary_groups) > 0) {
- fprintf(f, "%sSupplementaryGroups:", prefix);
- strv_fprintf(f, c->supplementary_groups);
- fputs("\n", f);
- }
-
- if (c->pam_name)
- fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
-
- if (strv_length(c->read_write_dirs) > 0) {
- fprintf(f, "%sReadWriteDirs:", prefix);
- strv_fprintf(f, c->read_write_dirs);
- fputs("\n", f);
- }
-
- if (strv_length(c->read_only_dirs) > 0) {
- fprintf(f, "%sReadOnlyDirs:", prefix);
- strv_fprintf(f, c->read_only_dirs);
- fputs("\n", f);
- }
-
- if (strv_length(c->inaccessible_dirs) > 0) {
- fprintf(f, "%sInaccessibleDirs:", prefix);
- strv_fprintf(f, c->inaccessible_dirs);
- fputs("\n", f);
- }
-
- fprintf(f,
- "%sKillMode: %s\n"
- "%sKillSignal: SIG%s\n"
- "%sSendSIGKILL: %s\n"
- "%sIgnoreSIGPIPE: %s\n",
- prefix, kill_mode_to_string(c->kill_mode),
- prefix, signal_to_string(c->kill_signal),
- prefix, yes_no(c->send_sigkill),
- prefix, yes_no(c->ignore_sigpipe));
-
- if (c->utmp_id)
- fprintf(f,
- "%sUtmpIdentifier: %s\n",
- prefix, c->utmp_id);
-}
-
-void exec_status_start(ExecStatus *s, pid_t pid) {
- assert(s);
-
- zero(*s);
- s->pid = pid;
- dual_timestamp_get(&s->start_timestamp);
-}
-
-void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status) {
- assert(s);
-
- if (s->pid && s->pid != pid)
- zero(*s);
-
- s->pid = pid;
- dual_timestamp_get(&s->exit_timestamp);
-
- s->code = code;
- s->status = status;
-
- if (context) {
- if (context->utmp_id)
- utmp_put_dead_process(context->utmp_id, pid, code, status);
-
- exec_context_tty_reset(context);
- }
-}
-
-void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
- char buf[FORMAT_TIMESTAMP_MAX];
-
- assert(s);
- assert(f);
-
- if (!prefix)
- prefix = "";
-
- if (s->pid <= 0)
- return;
-
- fprintf(f,
- "%sPID: %lu\n",
- prefix, (unsigned long) s->pid);
-
- if (s->start_timestamp.realtime > 0)
- fprintf(f,
- "%sStart Timestamp: %s\n",
- prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
-
- if (s->exit_timestamp.realtime > 0)
- fprintf(f,
- "%sExit Timestamp: %s\n"
- "%sExit Code: %s\n"
- "%sExit Status: %i\n",
- prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime),
- prefix, sigchld_code_to_string(s->code),
- prefix, s->status);
-}
-
-char *exec_command_line(char **argv) {
- size_t k;
- char *n, *p, **a;
- bool first = true;
-
- assert(argv);
-
- k = 1;
- STRV_FOREACH(a, argv)
- k += strlen(*a)+3;
-
- if (!(n = new(char, k)))
- return NULL;
-
- p = n;
- STRV_FOREACH(a, argv) {
-
- if (!first)
- *(p++) = ' ';
- else
- first = false;
-
- if (strpbrk(*a, WHITESPACE)) {
- *(p++) = '\'';
- p = stpcpy(p, *a);
- *(p++) = '\'';
- } else
- p = stpcpy(p, *a);
-
- }
-
- *p = 0;
-
- /* FIXME: this doesn't really handle arguments that have
- * spaces and ticks in them */
-
- return n;
-}
-
-void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
- char *p2;
- const char *prefix2;
-
- char *cmd;
-
- assert(c);
- assert(f);
-
- if (!prefix)
- prefix = "";
- p2 = strappend(prefix, "\t");
- prefix2 = p2 ? p2 : prefix;
-
- cmd = exec_command_line(c->argv);
-
- fprintf(f,
- "%sCommand Line: %s\n",
- prefix, cmd ? cmd : strerror(ENOMEM));
-
- free(cmd);
-
- exec_status_dump(&c->exec_status, f, prefix2);
-
- free(p2);
-}
-
-void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
- assert(f);
-
- if (!prefix)
- prefix = "";
-
- LIST_FOREACH(command, c, c)
- exec_command_dump(c, f, prefix);
-}
-
-void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
- ExecCommand *end;
-
- assert(l);
- assert(e);
-
- if (*l) {
- /* It's kind of important, that we keep the order here */
- LIST_FIND_TAIL(ExecCommand, command, *l, end);
- LIST_INSERT_AFTER(ExecCommand, command, *l, end, e);
- } else
- *l = e;
-}
-
-int exec_command_set(ExecCommand *c, const char *path, ...) {
- va_list ap;
- char **l, *p;
-
- assert(c);
- assert(path);
-
- va_start(ap, path);
- l = strv_new_ap(path, ap);
- va_end(ap);
-
- if (!l)
- return -ENOMEM;
-
- if (!(p = strdup(path))) {
- strv_free(l);
- return -ENOMEM;
- }
-
- free(c->path);
- c->path = p;
-
- strv_free(c->argv);
- c->argv = l;
-
- return 0;
-}
-
-static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
- [EXEC_INPUT_NULL] = "null",
- [EXEC_INPUT_TTY] = "tty",
- [EXEC_INPUT_TTY_FORCE] = "tty-force",
- [EXEC_INPUT_TTY_FAIL] = "tty-fail",
- [EXEC_INPUT_SOCKET] = "socket"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
-
-static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
- [EXEC_OUTPUT_INHERIT] = "inherit",
- [EXEC_OUTPUT_NULL] = "null",
- [EXEC_OUTPUT_TTY] = "tty",
- [EXEC_OUTPUT_SYSLOG] = "syslog",
- [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console",
- [EXEC_OUTPUT_KMSG] = "kmsg",
- [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
- [EXEC_OUTPUT_JOURNAL] = "journal",
- [EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
- [EXEC_OUTPUT_SOCKET] = "socket"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
-
-static const char* const kill_mode_table[_KILL_MODE_MAX] = {
- [KILL_CONTROL_GROUP] = "control-group",
- [KILL_PROCESS] = "process",
- [KILL_NONE] = "none"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
-
-static const char* const kill_who_table[_KILL_WHO_MAX] = {
- [KILL_MAIN] = "main",
- [KILL_CONTROL] = "control",
- [KILL_ALL] = "all"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
diff --git a/src/execute.h b/src/execute.h
deleted file mode 100644
index 0d7e7dd..0000000
--- a/src/execute.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooexecutehfoo
-#define fooexecutehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct ExecStatus ExecStatus;
-typedef struct ExecCommand ExecCommand;
-typedef struct ExecContext ExecContext;
-
-#include <linux/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/capability.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <sched.h>
-
-struct CGroupBonding;
-struct CGroupAttribute;
-
-#include "list.h"
-#include "util.h"
-
-typedef enum KillMode {
- KILL_CONTROL_GROUP = 0,
- KILL_PROCESS,
- KILL_NONE,
- _KILL_MODE_MAX,
- _KILL_MODE_INVALID = -1
-} KillMode;
-
-typedef enum KillWho {
- KILL_MAIN,
- KILL_CONTROL,
- KILL_ALL,
- _KILL_WHO_MAX,
- _KILL_WHO_INVALID = -1
-} KillWho;
-
-typedef enum ExecInput {
- EXEC_INPUT_NULL,
- EXEC_INPUT_TTY,
- EXEC_INPUT_TTY_FORCE,
- EXEC_INPUT_TTY_FAIL,
- EXEC_INPUT_SOCKET,
- _EXEC_INPUT_MAX,
- _EXEC_INPUT_INVALID = -1
-} ExecInput;
-
-typedef enum ExecOutput {
- EXEC_OUTPUT_INHERIT,
- EXEC_OUTPUT_NULL,
- EXEC_OUTPUT_TTY,
- EXEC_OUTPUT_SYSLOG,
- EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
- EXEC_OUTPUT_KMSG,
- EXEC_OUTPUT_KMSG_AND_CONSOLE,
- EXEC_OUTPUT_JOURNAL,
- EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
- EXEC_OUTPUT_SOCKET,
- _EXEC_OUTPUT_MAX,
- _EXEC_OUTPUT_INVALID = -1
-} ExecOutput;
-
-struct ExecStatus {
- dual_timestamp start_timestamp;
- dual_timestamp exit_timestamp;
- pid_t pid;
- int code; /* as in siginfo_t::si_code */
- int status; /* as in sigingo_t::si_status */
-};
-
-struct ExecCommand {
- char *path;
- char **argv;
- ExecStatus exec_status;
- LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
- bool ignore;
-};
-
-struct ExecContext {
- char **environment;
- char **environment_files;
-
- struct rlimit *rlimit[RLIMIT_NLIMITS];
- char *working_directory, *root_directory;
-
- mode_t umask;
- int oom_score_adjust;
- int nice;
- int ioprio;
- int cpu_sched_policy;
- int cpu_sched_priority;
-
- cpu_set_t *cpuset;
- unsigned cpuset_ncpus;
-
- ExecInput std_input;
- ExecOutput std_output;
- ExecOutput std_error;
-
- unsigned long timer_slack_nsec;
-
- char *tcpwrap_name;
-
- char *tty_path;
-
- bool tty_reset;
- bool tty_vhangup;
- bool tty_vt_disallocate;
-
- bool ignore_sigpipe;
-
- /* Since resolving these names might might involve socket
- * connections and we don't want to deadlock ourselves these
- * names are resolved on execution only and in the child
- * process. */
- char *user;
- char *group;
- char **supplementary_groups;
-
- char *pam_name;
-
- char *utmp_id;
-
- char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
- unsigned long mount_flags;
-
- uint64_t capability_bounding_set_drop;
-
- /* Not relevant for spawning processes, just for killing */
- KillMode kill_mode;
- int kill_signal;
- bool send_sigkill;
-
- cap_t capabilities;
- int secure_bits;
-
- int syslog_priority;
- char *syslog_identifier;
- bool syslog_level_prefix;
-
- bool cpu_sched_reset_on_fork;
- bool non_blocking;
- bool private_tmp;
- bool private_network;
-
- bool control_group_modify;
- int control_group_persistent;
-
- /* This is not exposed to the user but available
- * internally. We need it to make sure that whenever we spawn
- * /bin/mount it is run in the same process group as us so
- * that the autofs logic detects that it belongs to us and we
- * don't enter a trigger loop. */
- bool same_pgrp;
-
- bool oom_score_adjust_set:1;
- bool nice_set:1;
- bool ioprio_set:1;
- bool cpu_sched_set:1;
- bool timer_slack_nsec_set:1;
-};
-
-int exec_spawn(ExecCommand *command,
- char **argv,
- const ExecContext *context,
- int fds[], unsigned n_fds,
- char **environment,
- bool apply_permissions,
- bool apply_chroot,
- bool apply_tty_stdin,
- bool confirm_spawn,
- struct CGroupBonding *cgroup_bondings,
- struct CGroupAttribute *cgroup_attributes,
- pid_t *ret);
-
-void exec_command_done(ExecCommand *c);
-void exec_command_done_array(ExecCommand *c, unsigned n);
-
-void exec_command_free_list(ExecCommand *c);
-void exec_command_free_array(ExecCommand **c, unsigned n);
-
-char *exec_command_line(char **argv);
-
-void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix);
-void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix);
-void exec_command_append_list(ExecCommand **l, ExecCommand *e);
-int exec_command_set(ExecCommand *c, const char *path, ...);
-
-void exec_context_init(ExecContext *c);
-void exec_context_done(ExecContext *c);
-void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
-void exec_context_tty_reset(const ExecContext *context);
-
-int exec_context_load_environment(const ExecContext *c, char ***l);
-
-void exec_status_start(ExecStatus *s, pid_t pid);
-void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
-void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
-
-const char* exec_output_to_string(ExecOutput i);
-ExecOutput exec_output_from_string(const char *s);
-
-const char* exec_input_to_string(ExecInput i);
-ExecInput exec_input_from_string(const char *s);
-
-const char *kill_mode_to_string(KillMode k);
-KillMode kill_mode_from_string(const char *s);
-
-const char *kill_who_to_string(KillWho k);
-KillWho kill_who_from_string(const char *s);
-
-#endif
diff --git a/src/fdset.c b/src/fdset.c
deleted file mode 100644
index e67fe6f..0000000
--- a/src/fdset.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "set.h"
-#include "util.h"
-#include "macro.h"
-#include "fdset.h"
-
-#define MAKE_SET(s) ((Set*) s)
-#define MAKE_FDSET(s) ((FDSet*) s)
-
-/* Make sure we can distuingish fd 0 and NULL */
-#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
-#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
-
-FDSet *fdset_new(void) {
- return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func));
-}
-
-void fdset_free(FDSet *s) {
- void *p;
-
- while ((p = set_steal_first(MAKE_SET(s)))) {
- /* Valgrind's fd might have ended up in this set here,
- * due to fdset_new_fill(). We'll ignore all failures
- * here, so that the EBADFD that valgrind will return
- * us on close() doesn't influence us */
-
- /* When reloading duplicates of the private bus
- * connection fds and suchlike are closed here, which
- * has no effect at all, since they are only
- * duplicates. So don't be surprised about these log
- * messages. */
-
- log_debug("Closing left-over fd %i", PTR_TO_FD(p));
- close_nointr(PTR_TO_FD(p));
- }
-
- set_free(MAKE_SET(s));
-}
-
-int fdset_put(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_put(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_put_dup(FDSet *s, int fd) {
- int copy, r;
-
- assert(s);
- assert(fd >= 0);
-
- if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0)
- return -errno;
-
- if ((r = fdset_put(s, copy)) < 0) {
- close_nointr_nofail(copy);
- return r;
- }
-
- return copy;
-}
-
-bool fdset_contains(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_remove(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
-}
-
-int fdset_new_fill(FDSet **_s) {
- DIR *d;
- struct dirent *de;
- int r = 0;
- FDSet *s;
-
- assert(_s);
-
- /* Creates an fdsets and fills in all currently open file
- * descriptors. */
-
- if (!(d = opendir("/proc/self/fd")))
- return -errno;
-
- if (!(s = fdset_new())) {
- r = -ENOMEM;
- goto finish;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (ignore_file(de->d_name))
- continue;
-
- if ((r = safe_atoi(de->d_name, &fd)) < 0)
- goto finish;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if ((r = fdset_put(s, fd)) < 0)
- goto finish;
- }
-
- r = 0;
- *_s = s;
- s = NULL;
-
-finish:
- closedir(d);
-
- /* We won't close the fds here! */
- if (s)
- set_free(MAKE_SET(s));
-
- return r;
-}
-
-int fdset_cloexec(FDSet *fds, bool b) {
- Iterator i;
- void *p;
- int r;
-
- assert(fds);
-
- SET_FOREACH(p, MAKE_SET(fds), i)
- if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
- return r;
-
- return 0;
-}
diff --git a/src/fdset.h b/src/fdset.h
deleted file mode 100644
index 044a9e6..0000000
--- a/src/fdset.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foofdsethfoo
-#define foofdsethfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct FDSet FDSet;
-
-FDSet* fdset_new(void);
-void fdset_free(FDSet *s);
-
-int fdset_put(FDSet *s, int fd);
-int fdset_put_dup(FDSet *s, int fd);
-
-bool fdset_contains(FDSet *s, int fd);
-int fdset_remove(FDSet *s, int fd);
-
-int fdset_new_fill(FDSet **_s);
-
-int fdset_cloexec(FDSet *fds, bool b);
-
-#endif
diff --git a/src/ima-setup.c b/src/ima-setup.c
deleted file mode 100644
index 03e43dc..0000000
--- a/src/ima-setup.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include "ima-setup.h"
-#include "mount-setup.h"
-#include "macro.h"
-#include "util.h"
-#include "log.h"
-#include "label.h"
-
-#define IMA_SECFS_DIR "/sys/kernel/security/ima"
-#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy"
-#define IMA_POLICY_PATH "/etc/ima/ima-policy"
-
-int ima_setup(void) {
-
-#ifdef HAVE_IMA
- struct stat st;
- ssize_t policy_size = 0, written = 0;
- char *policy;
- int policyfd = -1, imafd = -1;
- int result = 0;
-
-#ifndef HAVE_SELINUX
- /* Mount the securityfs filesystem */
- mount_setup_early();
-#endif
-
- if (stat(IMA_POLICY_PATH, &st) < 0)
- return 0;
-
- policy_size = st.st_size;
- if (stat(IMA_SECFS_DIR, &st) < 0) {
- log_debug("IMA support is disabled in the kernel, ignoring.");
- return 0;
- }
-
- if (stat(IMA_SECFS_POLICY, &st) < 0) {
- log_error("Another IMA custom policy has already been loaded, "
- "ignoring.");
- return 0;
- }
-
- policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC);
- if (policyfd < 0) {
- log_error("Failed to open the IMA custom policy file %s (%m), "
- "ignoring.", IMA_POLICY_PATH);
- return 0;
- }
-
- imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC);
- if (imafd < 0) {
- log_error("Failed to open the IMA kernel interface %s (%m), "
- "ignoring.", IMA_SECFS_POLICY);
- goto out;
- }
-
- policy = mmap(NULL, policy_size, PROT_READ, MAP_PRIVATE, policyfd, 0);
- if (policy == MAP_FAILED) {
- log_error("mmap() failed (%m), freezing");
- result = -errno;
- goto out;
- }
-
- written = loop_write(imafd, policy, (size_t)policy_size, false);
- if (written != policy_size) {
- log_error("Failed to load the IMA custom policy file %s (%m), "
- "ignoring.", IMA_POLICY_PATH);
- goto out_mmap;
- }
-
- log_info("Successfully loaded the IMA custom policy %s.",
- IMA_POLICY_PATH);
-out_mmap:
- munmap(policy, policy_size);
-out:
- if (policyfd >= 0)
- close_nointr_nofail(policyfd);
- if (imafd >= 0)
- close_nointr_nofail(imafd);
- if (result)
- return result;
-#endif /* HAVE_IMA */
-
- return 0;
-}
diff --git a/src/ima-setup.h b/src/ima-setup.h
deleted file mode 100644
index 7d677cf..0000000
--- a/src/ima-setup.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooimasetuphfoo
-#define fooimasetuphfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int ima_setup(void);
-
-#endif
diff --git a/src/initreq.h b/src/initreq.h
deleted file mode 100644
index 859042c..0000000
--- a/src/initreq.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * initreq.h Interface to talk to init through /dev/initctl.
- *
- * Copyright (C) 1995-2004 Miquel van Smoorenburg
- *
- * This library 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 of the License, or (at your option) any later version.
- *
- * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS
- *
- */
-#ifndef _INITREQ_H
-#define _INITREQ_H
-
-#include <sys/param.h>
-
-#if defined(__FreeBSD_kernel__)
-# define INIT_FIFO "/etc/.initctl"
-#else
-# define INIT_FIFO "/dev/initctl"
-#endif
-
-#define INIT_MAGIC 0x03091969
-#define INIT_CMD_START 0
-#define INIT_CMD_RUNLVL 1
-#define INIT_CMD_POWERFAIL 2
-#define INIT_CMD_POWERFAILNOW 3
-#define INIT_CMD_POWEROK 4
-#define INIT_CMD_BSD 5
-#define INIT_CMD_SETENV 6
-#define INIT_CMD_UNSETENV 7
-
-#define INIT_CMD_CHANGECONS 12345
-
-#ifdef MAXHOSTNAMELEN
-# define INITRQ_HLEN MAXHOSTNAMELEN
-#else
-# define INITRQ_HLEN 64
-#endif
-
-/*
- * This is what BSD 4.4 uses when talking to init.
- * Linux doesn't use this right now.
- */
-struct init_request_bsd {
- char gen_id[8]; /* Beats me.. telnetd uses "fe" */
- char tty_id[16]; /* Tty name minus /dev/tty */
- char host[INITRQ_HLEN]; /* Hostname */
- char term_type[16]; /* Terminal type */
- int signal; /* Signal to send */
- int pid; /* Process to send to */
- char exec_name[128]; /* Program to execute */
- char reserved[128]; /* For future expansion. */
-};
-
-
-/*
- * Because of legacy interfaces, "runlevel" and "sleeptime"
- * aren't in a separate struct in the union.
- *
- * The weird sizes are because init expects the whole
- * struct to be 384 bytes.
- */
-struct init_request {
- int magic; /* Magic number */
- int cmd; /* What kind of request */
- int runlevel; /* Runlevel to change to */
- int sleeptime; /* Time between TERM and KILL */
- union {
- struct init_request_bsd bsd;
- char data[368];
- } i;
-};
-
-#endif
diff --git a/src/job.c b/src/job.c
deleted file mode 100644
index bae6ab9..0000000
--- a/src/job.c
+++ /dev/null
@@ -1,701 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <errno.h>
-#include <sys/timerfd.h>
-#include <sys/epoll.h>
-
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "dbus-job.h"
-
-Job* job_new(Manager *m, JobType type, Unit *unit) {
- Job *j;
-
- assert(m);
- assert(type < _JOB_TYPE_MAX);
- assert(unit);
-
- if (!(j = new0(Job, 1)))
- return NULL;
-
- j->manager = m;
- j->id = m->current_job_id++;
- j->type = type;
- j->unit = unit;
-
- j->timer_watch.type = WATCH_INVALID;
-
- /* We don't link it here, that's what job_dependency() is for */
-
- return j;
-}
-
-void job_free(Job *j) {
- assert(j);
-
- /* Detach from next 'bigger' objects */
- if (j->installed) {
- bus_job_send_removed_signal(j);
-
- if (j->unit->job == j) {
- j->unit->job = NULL;
- unit_add_to_gc_queue(j->unit);
- }
-
- hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
- j->installed = false;
- }
-
- /* Detach from next 'smaller' objects */
- manager_transaction_unlink_job(j->manager, j, true);
-
- if (j->in_run_queue)
- LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
-
- if (j->in_dbus_queue)
- LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
-
- if (j->timer_watch.type != WATCH_INVALID) {
- assert(j->timer_watch.type == WATCH_JOB_TIMER);
- assert(j->timer_watch.data.job == j);
- assert(j->timer_watch.fd >= 0);
-
- assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0);
- close_nointr_nofail(j->timer_watch.fd);
- }
-
- free(j->bus_client);
- free(j);
-}
-
-JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
- JobDependency *l;
-
- assert(object);
-
- /* Adds a new job link, which encodes that the 'subject' job
- * needs the 'object' job in some way. If 'subject' is NULL
- * this means the 'anchor' job (i.e. the one the user
- * explicitly asked for) is the requester. */
-
- if (!(l = new0(JobDependency, 1)))
- return NULL;
-
- l->subject = subject;
- l->object = object;
- l->matters = matters;
- l->conflicts = conflicts;
-
- if (subject)
- LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
- else
- LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
-
- LIST_PREPEND(JobDependency, object, object->object_list, l);
-
- return l;
-}
-
-void job_dependency_free(JobDependency *l) {
- assert(l);
-
- if (l->subject)
- LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
- else
- LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
-
- LIST_REMOVE(JobDependency, object, l->object->object_list, l);
-
- free(l);
-}
-
-void job_dump(Job *j, FILE*f, const char *prefix) {
- assert(j);
- assert(f);
-
- if (!prefix)
- prefix = "";
-
- fprintf(f,
- "%s-> Job %u:\n"
- "%s\tAction: %s -> %s\n"
- "%s\tState: %s\n"
- "%s\tForced: %s\n",
- prefix, j->id,
- prefix, j->unit->id, job_type_to_string(j->type),
- prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override));
-}
-
-bool job_is_anchor(Job *j) {
- JobDependency *l;
-
- assert(j);
-
- LIST_FOREACH(object, l, j->object_list)
- if (!l->subject)
- return true;
-
- return false;
-}
-
-/*
- * Merging is commutative, so imagine the matrix as symmetric. We store only
- * its lower triangle to avoid duplication. We don't store the main diagonal,
- * because A merged with A is simply A.
- *
- * Merging is associative! A merged with B merged with C is the same as
- * A merged with C merged with B.
- *
- * Mergeability is transitive! If A can be merged with B and B with C then
- * A also with C.
- *
- * Also, if A merged with B cannot be merged with C, then either A or B cannot
- * be merged with C either.
- */
-static const JobType job_merging_table[] = {
-/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD JOB_RELOAD_OR_START JOB_RESTART JOB_TRY_RESTART */
-/************************************************************************************************************************************/
-/*JOB_START */
-/*JOB_VERIFY_ACTIVE */ JOB_START,
-/*JOB_STOP */ -1, -1,
-/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1,
-/*JOB_RELOAD_OR_START*/ JOB_RELOAD_OR_START, JOB_RELOAD_OR_START, -1, JOB_RELOAD_OR_START,
-/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, JOB_RESTART,
-/*JOB_TRY_RESTART */ JOB_RESTART, JOB_TRY_RESTART, -1, JOB_TRY_RESTART, JOB_RESTART, JOB_RESTART,
-};
-
-JobType job_type_lookup_merge(JobType a, JobType b) {
- assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX * (_JOB_TYPE_MAX - 1) / 2);
- assert(a >= 0 && a < _JOB_TYPE_MAX);
- assert(b >= 0 && b < _JOB_TYPE_MAX);
-
- if (a == b)
- return a;
-
- if (a < b) {
- JobType tmp = a;
- a = b;
- b = tmp;
- }
-
- return job_merging_table[(a - 1) * a / 2 + b];
-}
-
-bool job_type_is_redundant(JobType a, UnitActiveState b) {
- switch (a) {
-
- case JOB_START:
- return
- b == UNIT_ACTIVE ||
- b == UNIT_RELOADING;
-
- case JOB_STOP:
- return
- b == UNIT_INACTIVE ||
- b == UNIT_FAILED;
-
- case JOB_VERIFY_ACTIVE:
- return
- b == UNIT_ACTIVE ||
- b == UNIT_RELOADING;
-
- case JOB_RELOAD:
- return
- b == UNIT_RELOADING;
-
- case JOB_RELOAD_OR_START:
- return
- b == UNIT_ACTIVATING ||
- b == UNIT_RELOADING;
-
- case JOB_RESTART:
- return
- b == UNIT_ACTIVATING;
-
- case JOB_TRY_RESTART:
- return
- b == UNIT_ACTIVATING;
-
- default:
- assert_not_reached("Invalid job type");
- }
-}
-
-bool job_is_runnable(Job *j) {
- Iterator i;
- Unit *other;
-
- assert(j);
- assert(j->installed);
-
- /* Checks whether there is any job running for the units this
- * job needs to be running after (in the case of a 'positive'
- * job type) or before (in the case of a 'negative' job
- * type. */
-
- /* First check if there is an override */
- if (j->ignore_order)
- return true;
-
- if (j->type == JOB_START ||
- j->type == JOB_VERIFY_ACTIVE ||
- j->type == JOB_RELOAD ||
- j->type == JOB_RELOAD_OR_START) {
-
- /* Immediate result is that the job is or might be
- * started. In this case lets wait for the
- * dependencies, regardless whether they are
- * starting or stopping something. */
-
- SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
- if (other->job)
- return false;
- }
-
- /* Also, if something else is being stopped and we should
- * change state after it, then lets wait. */
-
- SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
- if (other->job &&
- (other->job->type == JOB_STOP ||
- other->job->type == JOB_RESTART ||
- other->job->type == JOB_TRY_RESTART))
- return false;
-
- /* This means that for a service a and a service b where b
- * shall be started after a:
- *
- * start a + start b â 1st step start a, 2nd step start b
- * start a + stop b â 1st step stop b, 2nd step start a
- * stop a + start b â 1st step stop a, 2nd step start b
- * stop a + stop b â 1st step stop b, 2nd step stop a
- *
- * This has the side effect that restarts are properly
- * synchronized too. */
-
- return true;
-}
-
-static void job_change_type(Job *j, JobType newtype) {
- log_debug("Converting job %s/%s -> %s/%s",
- j->unit->id, job_type_to_string(j->type),
- j->unit->id, job_type_to_string(newtype));
-
- j->type = newtype;
-}
-
-int job_run_and_invalidate(Job *j) {
- int r;
- uint32_t id;
- Manager *m;
-
- assert(j);
- assert(j->installed);
-
- if (j->in_run_queue) {
- LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
- j->in_run_queue = false;
- }
-
- if (j->state != JOB_WAITING)
- return 0;
-
- if (!job_is_runnable(j))
- return -EAGAIN;
-
- j->state = JOB_RUNNING;
- job_add_to_dbus_queue(j);
-
- /* While we execute this operation the job might go away (for
- * example: because it is replaced by a new, conflicting
- * job.) To make sure we don't access a freed job later on we
- * store the id here, so that we can verify the job is still
- * valid. */
- id = j->id;
- m = j->manager;
-
- switch (j->type) {
-
- case JOB_RELOAD_OR_START:
- if (unit_active_state(j->unit) == UNIT_ACTIVE) {
- job_change_type(j, JOB_RELOAD);
- r = unit_reload(j->unit);
- break;
- }
- job_change_type(j, JOB_START);
- /* fall through */
-
- case JOB_START:
- r = unit_start(j->unit);
-
- /* If this unit cannot be started, then simply wait */
- if (r == -EBADR)
- r = 0;
- break;
-
- case JOB_VERIFY_ACTIVE: {
- UnitActiveState t = unit_active_state(j->unit);
- if (UNIT_IS_ACTIVE_OR_RELOADING(t))
- r = -EALREADY;
- else if (t == UNIT_ACTIVATING)
- r = -EAGAIN;
- else
- r = -ENOEXEC;
- break;
- }
-
- case JOB_TRY_RESTART:
- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) {
- r = -ENOEXEC;
- break;
- }
- job_change_type(j, JOB_RESTART);
- /* fall through */
-
- case JOB_STOP:
- case JOB_RESTART:
- r = unit_stop(j->unit);
-
- /* If this unit cannot stopped, then simply wait. */
- if (r == -EBADR)
- r = 0;
- break;
-
- case JOB_RELOAD:
- r = unit_reload(j->unit);
- break;
-
- default:
- assert_not_reached("Unknown job type");
- }
-
- if ((j = manager_get_job(m, id))) {
- if (r == -EALREADY)
- r = job_finish_and_invalidate(j, JOB_DONE);
- else if (r == -ENOEXEC)
- r = job_finish_and_invalidate(j, JOB_SKIPPED);
- else if (r == -EAGAIN)
- j->state = JOB_WAITING;
- else if (r < 0)
- r = job_finish_and_invalidate(j, JOB_FAILED);
- }
-
- return r;
-}
-
-static void job_print_status_message(Unit *u, JobType t, JobResult result) {
- assert(u);
-
- if (t == JOB_START) {
-
- switch (result) {
-
- case JOB_DONE:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
- break;
-
- case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
- unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
- break;
-
- case JOB_DEPENDENCY:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
- break;
-
- case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
- break;
-
- default:
- ;
- }
-
- } else if (t == JOB_STOP) {
-
- switch (result) {
-
- case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
- break;
-
- case JOB_DONE:
- case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
- break;
-
- default:
- ;
- }
- }
-}
-
-int job_finish_and_invalidate(Job *j, JobResult result) {
- Unit *u;
- Unit *other;
- JobType t;
- Iterator i;
- bool recursed = false;
-
- assert(j);
- assert(j->installed);
-
- job_add_to_dbus_queue(j);
-
- /* Patch restart jobs so that they become normal start jobs */
- if (result == JOB_DONE && j->type == JOB_RESTART) {
-
- job_change_type(j, JOB_START);
- j->state = JOB_WAITING;
-
- job_add_to_run_queue(j);
-
- u = j->unit;
- goto finish;
- }
-
- j->result = result;
-
- log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
-
- if (result == JOB_FAILED)
- j->manager->n_failed_jobs ++;
-
- u = j->unit;
- t = j->type;
- job_free(j);
-
- job_print_status_message(u, t, result);
-
- /* Fail depending jobs on failure */
- if (result != JOB_DONE) {
-
- if (t == JOB_START ||
- t == JOB_VERIFY_ACTIVE ||
- t == JOB_RELOAD_OR_START) {
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE ||
- other->job->type == JOB_RELOAD_OR_START)) {
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
- recursed = true;
- }
-
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE ||
- other->job->type == JOB_RELOAD_OR_START)) {
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
- recursed = true;
- }
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (other->job &&
- !other->job->override &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE ||
- other->job->type == JOB_RELOAD_OR_START)) {
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
- recursed = true;
- }
-
- } else if (t == JOB_STOP) {
-
- SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE ||
- other->job->type == JOB_RELOAD_OR_START)) {
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
- recursed = true;
- }
- }
- }
-
- /* Trigger OnFailure dependencies that are not generated by
- * the unit itself. We don't tread JOB_CANCELED as failure in
- * this context. And JOB_FAILURE is already handled by the
- * unit itself. */
- if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
- log_notice("Job %s/%s failed with result '%s'.",
- u->id,
- job_type_to_string(t),
- job_result_to_string(result));
-
- unit_trigger_on_failure(u);
- }
-
-finish:
- /* Try to start the next jobs that can be started */
- SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
- if (other->job)
- job_add_to_run_queue(other->job);
- SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
- if (other->job)
- job_add_to_run_queue(other->job);
-
- manager_check_finished(u->manager);
-
- return recursed;
-}
-
-int job_start_timer(Job *j) {
- struct itimerspec its;
- struct epoll_event ev;
- int fd, r;
- assert(j);
-
- if (j->unit->job_timeout <= 0 ||
- j->timer_watch.type == WATCH_JOB_TIMER)
- return 0;
-
- assert(j->timer_watch.type == WATCH_INVALID);
-
- if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
- r = -errno;
- goto fail;
- }
-
- zero(its);
- timespec_store(&its.it_value, j->unit->job_timeout);
-
- if (timerfd_settime(fd, 0, &its, NULL) < 0) {
- r = -errno;
- goto fail;
- }
-
- zero(ev);
- ev.data.ptr = &j->timer_watch;
- ev.events = EPOLLIN;
-
- if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
- r = -errno;
- goto fail;
- }
-
- j->timer_watch.type = WATCH_JOB_TIMER;
- j->timer_watch.fd = fd;
- j->timer_watch.data.job = j;
-
- return 0;
-
-fail:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
-
-void job_add_to_run_queue(Job *j) {
- assert(j);
- assert(j->installed);
-
- if (j->in_run_queue)
- return;
-
- LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
- j->in_run_queue = true;
-}
-
-void job_add_to_dbus_queue(Job *j) {
- assert(j);
- assert(j->installed);
-
- if (j->in_dbus_queue)
- return;
-
- /* We don't check if anybody is subscribed here, since this
- * job might just have been created and not yet assigned to a
- * connection/client. */
-
- LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
- j->in_dbus_queue = true;
-}
-
-char *job_dbus_path(Job *j) {
- char *p;
-
- assert(j);
-
- if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
- return NULL;
-
- return p;
-}
-
-void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
- assert(j);
- assert(w == &j->timer_watch);
-
- log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
- job_finish_and_invalidate(j, JOB_TIMEOUT);
-}
-
-static const char* const job_state_table[_JOB_STATE_MAX] = {
- [JOB_WAITING] = "waiting",
- [JOB_RUNNING] = "running"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
-
-static const char* const job_type_table[_JOB_TYPE_MAX] = {
- [JOB_START] = "start",
- [JOB_VERIFY_ACTIVE] = "verify-active",
- [JOB_STOP] = "stop",
- [JOB_RELOAD] = "reload",
- [JOB_RELOAD_OR_START] = "reload-or-start",
- [JOB_RESTART] = "restart",
- [JOB_TRY_RESTART] = "try-restart",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
-
-static const char* const job_mode_table[_JOB_MODE_MAX] = {
- [JOB_FAIL] = "fail",
- [JOB_REPLACE] = "replace",
- [JOB_ISOLATE] = "isolate",
- [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
- [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
-
-static const char* const job_result_table[_JOB_RESULT_MAX] = {
- [JOB_DONE] = "done",
- [JOB_CANCELED] = "canceled",
- [JOB_TIMEOUT] = "timeout",
- [JOB_FAILED] = "failed",
- [JOB_DEPENDENCY] = "dependency",
- [JOB_SKIPPED] = "skipped"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
diff --git a/src/job.h b/src/job.h
deleted file mode 100644
index 60a43e0..0000000
--- a/src/job.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foojobhfoo
-#define foojobhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <inttypes.h>
-#include <errno.h>
-
-typedef struct Job Job;
-typedef struct JobDependency JobDependency;
-typedef enum JobType JobType;
-typedef enum JobState JobState;
-typedef enum JobMode JobMode;
-typedef enum JobResult JobResult;
-
-#include "manager.h"
-#include "unit.h"
-#include "hashmap.h"
-#include "list.h"
-
-/* Be careful when changing the job types! Adjust job_merging_table[] accordingly! */
-enum JobType {
- JOB_START, /* if a unit does not support being started, we'll just wait until it becomes active */
- JOB_VERIFY_ACTIVE,
-
- JOB_STOP,
-
- JOB_RELOAD, /* if running reload */
- JOB_RELOAD_OR_START, /* if running reload, if not running start */
-
- /* Note that restarts are first treated like JOB_STOP, but
- * then instead of finishing are patched to become
- * JOB_START. */
- JOB_RESTART, /* if running stop, then start unconditionally */
- JOB_TRY_RESTART, /* if running stop and then start */
-
- _JOB_TYPE_MAX,
- _JOB_TYPE_INVALID = -1
-};
-
-enum JobState {
- JOB_WAITING,
- JOB_RUNNING,
- _JOB_STATE_MAX,
- _JOB_STATE_INVALID = -1
-};
-
-enum JobMode {
- JOB_FAIL, /* Fail if a conflicting job is already queued */
- JOB_REPLACE, /* Replace an existing conflicting job */
- JOB_ISOLATE, /* Start a unit, and stop all others */
- JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
- JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
- _JOB_MODE_MAX,
- _JOB_MODE_INVALID = -1
-};
-
-enum JobResult {
- JOB_DONE,
- JOB_CANCELED,
- JOB_TIMEOUT,
- JOB_FAILED,
- JOB_DEPENDENCY,
- JOB_SKIPPED,
- _JOB_RESULT_MAX,
- _JOB_RESULT_INVALID = -1
-};
-
-struct JobDependency {
- /* Encodes that the 'subject' job needs the 'object' job in
- * some way. This structure is used only while building a transaction. */
- Job *subject;
- Job *object;
-
- LIST_FIELDS(JobDependency, subject);
- LIST_FIELDS(JobDependency, object);
-
- bool matters;
- bool conflicts;
-};
-
-struct Job {
- Manager *manager;
- Unit *unit;
-
- LIST_FIELDS(Job, transaction);
- LIST_FIELDS(Job, run_queue);
- LIST_FIELDS(Job, dbus_queue);
-
- LIST_HEAD(JobDependency, subject_list);
- LIST_HEAD(JobDependency, object_list);
-
- /* Used for graph algs as a "I have been here" marker */
- Job* marker;
- unsigned generation;
-
- uint32_t id;
-
- JobType type;
- JobState state;
-
- Watch timer_watch;
-
- /* Note that this bus object is not ref counted here. */
- DBusConnection *bus;
- char *bus_client;
-
- JobResult result;
-
- bool installed:1;
- bool in_run_queue:1;
- bool matters_to_anchor:1;
- bool override:1;
- bool in_dbus_queue:1;
- bool sent_dbus_new_signal:1;
- bool ignore_order:1;
-};
-
-Job* job_new(Manager *m, JobType type, Unit *unit);
-void job_free(Job *job);
-void job_dump(Job *j, FILE*f, const char *prefix);
-
-JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
-void job_dependency_free(JobDependency *l);
-
-bool job_is_anchor(Job *j);
-
-int job_merge(Job *j, Job *other);
-
-JobType job_type_lookup_merge(JobType a, JobType b);
-
-static inline int job_type_merge(JobType *a, JobType b) {
- JobType t = job_type_lookup_merge(*a, b);
- if (t < 0)
- return -EEXIST;
- *a = t;
- return 0;
-}
-
-static inline bool job_type_is_mergeable(JobType a, JobType b) {
- return job_type_lookup_merge(a, b) >= 0;
-}
-
-static inline bool job_type_is_conflicting(JobType a, JobType b) {
- return !job_type_is_mergeable(a, b);
-}
-
-static inline bool job_type_is_superset(JobType a, JobType b) {
- /* Checks whether operation a is a "superset" of b in its actions */
- return a == job_type_lookup_merge(a, b);
-}
-
-bool job_type_is_redundant(JobType a, UnitActiveState b);
-
-bool job_is_runnable(Job *j);
-
-void job_add_to_run_queue(Job *j);
-void job_add_to_dbus_queue(Job *j);
-
-int job_start_timer(Job *j);
-void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w);
-
-int job_run_and_invalidate(Job *j);
-int job_finish_and_invalidate(Job *j, JobResult result);
-
-char *job_dbus_path(Job *j);
-
-const char* job_type_to_string(JobType t);
-JobType job_type_from_string(const char *s);
-
-const char* job_state_to_string(JobState t);
-JobState job_state_from_string(const char *s);
-
-const char* job_mode_to_string(JobMode t);
-JobMode job_mode_from_string(const char *s);
-
-const char* job_result_to_string(JobResult t);
-JobResult job_result_from_string(const char *s);
-
-#endif
diff --git a/src/kmod-setup.c b/src/kmod-setup.c
deleted file mode 100644
index debf871..0000000
--- a/src/kmod-setup.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/wait.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <libkmod.h>
-
-#include "macro.h"
-#include "execute.h"
-
-#include "kmod-setup.h"
-
-static const char * const kmod_table[] = {
- "autofs4", "/sys/class/misc/autofs",
- "ipv6", "/sys/module/ipv6",
- "unix", "/proc/net/unix"
-};
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
- const char *fn, const char *format, va_list args)
-{
- log_meta(priority, file, line, fn, format, args);
-}
-#pragma GCC diagnostic pop
-
-int kmod_setup(void) {
- unsigned i;
- struct kmod_ctx *ctx = NULL;
- struct kmod_module *mod;
- int err;
-
- for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) {
-
- if (access(kmod_table[i+1], F_OK) >= 0)
- continue;
-
- log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. "
- "We'll now try to work around this by loading the module...",
- kmod_table[i]);
-
- if (!ctx) {
- ctx = kmod_new(NULL, NULL);
- if (!ctx) {
- log_error("Failed to allocate memory for kmod");
- return -ENOMEM;
- }
-
- kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
-
- kmod_load_resources(ctx);
- }
-
- err = kmod_module_new_from_name(ctx, kmod_table[i], &mod);
- if (err < 0) {
- log_error("Failed to load module '%s'", kmod_table[i]);
- continue;
- }
-
- err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
- if (err == 0)
- log_info("Inserted module '%s'", kmod_module_get_name(mod));
- else if (err == KMOD_PROBE_APPLY_BLACKLIST)
- log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
- else
- log_error("Failed to insert '%s'", kmod_module_get_name(mod));
-
- kmod_module_unref(mod);
- }
-
- if (ctx)
- kmod_unref(ctx);
-
- return 0;
-}
diff --git a/src/kmod-setup.h b/src/kmod-setup.h
deleted file mode 100644
index 496aef3..0000000
--- a/src/kmod-setup.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fookmodsetuphfoo
-#define fookmodsetuphfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int kmod_setup(void);
-
-#endif
diff --git a/src/load-dropin.c b/src/load-dropin.c
deleted file mode 100644
index d869ee0..0000000
--- a/src/load-dropin.c
+++ /dev/null
@@ -1,150 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "unit.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "unit-name.h"
-
-static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) {
- DIR *d;
- struct dirent *de;
- int r;
-
- assert(u);
- assert(path);
-
- d = opendir(path);
- if (!d) {
-
- if (errno == ENOENT)
- return 0;
-
- return -errno;
- }
-
- while ((de = readdir(d))) {
- char *f;
-
- if (ignore_file(de->d_name))
- continue;
-
- f = join(path, "/", de->d_name, NULL);
- if (!f) {
- r = -ENOMEM;
- goto finish;
- }
-
- r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true);
- free(f);
-
- if (r < 0)
- log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r));
- }
-
- r = 0;
-
-finish:
- closedir(d);
- return r;
-}
-
-static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) {
- int r;
- char *path;
-
- assert(u);
- assert(unit_path);
- assert(name);
- assert(suffix);
-
- path = join(unit_path, "/", name, suffix, NULL);
- if (!path)
- return -ENOMEM;
-
- if (u->manager->unit_path_cache &&
- !set_get(u->manager->unit_path_cache, path))
- r = 0;
- else
- r = iterate_dir(u, path, dependency);
- free(path);
-
- if (r < 0)
- return r;
-
- if (u->instance) {
- char *template;
- /* Also try the template dir */
-
- template = unit_name_template(name);
- if (!template)
- return -ENOMEM;
-
- path = join(unit_path, "/", template, suffix, NULL);
- free(template);
-
- if (!path)
- return -ENOMEM;
-
- if (u->manager->unit_path_cache &&
- !set_get(u->manager->unit_path_cache, path))
- r = 0;
- else
- r = iterate_dir(u, path, dependency);
- free(path);
-
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int unit_load_dropin(Unit *u) {
- Iterator i;
- char *t;
-
- assert(u);
-
- /* Load dependencies from supplementary drop-in directories */
-
- SET_FOREACH(t, u->names, i) {
- char **p;
-
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
- int r;
-
- r = process_dir(u, *p, t, ".wants", UNIT_WANTS);
- if (r < 0)
- return r;
-
- r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES);
- if (r < 0)
- return r;
- }
- }
-
- return 0;
-}
diff --git a/src/load-dropin.h b/src/load-dropin.h
deleted file mode 100644
index cf3a799..0000000
--- a/src/load-dropin.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooloaddropinhfoo
-#define fooloaddropinhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "unit.h"
-
-/* Read service data supplementary drop-in directories */
-
-int unit_load_dropin(Unit *u);
-
-#endif
diff --git a/src/load-fragment.c b/src/load-fragment.c
deleted file mode 100644
index 637c82b..0000000
--- a/src/load-fragment.c
+++ /dev/null
@@ -1,2445 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <linux/oom.h>
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/prctl.h>
-#include <sys/mount.h>
-#include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include "unit.h"
-#include "strv.h"
-#include "conf-parser.h"
-#include "load-fragment.h"
-#include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
-#include "missing.h"
-#include "unit-name.h"
-#include "bus-errors.h"
-#include "utf8.h"
-
-#ifndef HAVE_SYSV_COMPAT
-int config_parse_warn_compat(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
- return 0;
-}
-#endif
-
-int config_parse_unit_deps(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- UnitDependency d = ltype;
- Unit *u = userdata;
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
- int r;
-
- t = strndup(w, l);
- if (!t)
- return -ENOMEM;
-
- k = unit_name_printf(u, t);
- free(t);
- if (!k)
- return -ENOMEM;
-
- r = unit_add_dependency_by_name(u, d, k, NULL, true);
- if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
-
- free(k);
- }
-
- return 0;
-}
-
-int config_parse_unit_names(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
- int r;
-
- t = strndup(w, l);
- if (!t)
- return -ENOMEM;
-
- k = unit_name_printf(u, t);
- free(t);
- if (!k)
- return -ENOMEM;
-
- r = unit_merge_by_name(u, k);
- if (r < 0)
- log_error("Failed to add name %s, ignoring: %s", k, strerror(-r));
-
- free(k);
- }
-
- return 0;
-}
-
-int config_parse_unit_string_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *k;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(u);
-
- k = unit_full_printf(u, rvalue);
- if (!k)
- return -ENOMEM;
-
- r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
- free (k);
-
- return r;
-}
-
-int config_parse_unit_strv_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *k;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(u);
-
- k = unit_full_printf(u, rvalue);
- if (!k)
- return -ENOMEM;
-
- r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
-
- return r;
-}
-
-int config_parse_unit_path_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *k;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(u);
-
- k = unit_full_printf(u, rvalue);
- if (!k)
- return -ENOMEM;
-
- r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
-
- return r;
-}
-
-int config_parse_socket_listen(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- SocketPort *p, *tail;
- Socket *s;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- s = SOCKET(data);
-
- p = new0(SocketPort, 1);
- if (!p)
- return -ENOMEM;
-
- if (streq(lvalue, "ListenFIFO")) {
- p->type = SOCKET_FIFO;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenSpecial")) {
- p->type = SOCKET_SPECIAL;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenMessageQueue")) {
-
- p->type = SOCKET_MQUEUE;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenNetlink")) {
- char *k;
- int r;
-
- p->type = SOCKET_SOCKET;
- k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse_netlink(&p->address, k);
- free(k);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
- free(p);
- return 0;
- }
-
- } else {
- char *k;
- int r;
-
- p->type = SOCKET_SOCKET;
- k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse(&p->address, k);
- free(k);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
- free(p);
- return 0;
- }
-
- if (streq(lvalue, "ListenStream"))
- p->address.type = SOCK_STREAM;
- else if (streq(lvalue, "ListenDatagram"))
- p->address.type = SOCK_DGRAM;
- else {
- assert(streq(lvalue, "ListenSequentialPacket"));
- p->address.type = SOCK_SEQPACKET;
- }
-
- if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
- log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
- free(p);
- return 0;
- }
- }
-
- p->fd = -1;
-
- if (s->ports) {
- LIST_FIND_TAIL(SocketPort, port, s->ports, tail);
- LIST_INSERT_AFTER(SocketPort, port, s->ports, tail, p);
- } else
- LIST_PREPEND(SocketPort, port, s->ports, p);
-
- return 0;
-}
-
-int config_parse_socket_bind(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Socket *s;
- SocketAddressBindIPv6Only b;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- s = SOCKET(data);
-
- if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) {
- int r;
-
- if ((r = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH;
- } else
- s->bind_ipv6_only = b;
-
- return 0;
-}
-
-int config_parse_exec_nice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- int priority;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atoi(rvalue, &priority) < 0) {
- log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
- return 0;
- }
-
- if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->nice = priority;
- c->nice_set = true;
-
- return 0;
-}
-
-int config_parse_exec_oom_score_adjust(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- int oa;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atoi(rvalue, &oa) < 0) {
- log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
- log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->oom_score_adjust = oa;
- c->oom_score_adjust_set = true;
-
- return 0;
-}
-
-int config_parse_exec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecCommand **e = data, *nce;
- char *path, **n;
- unsigned k;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(e);
-
- /* We accept an absolute path as first argument, or
- * alternatively an absolute prefixed with @ to allow
- * overriding of argv[0]. */
-
- e += ltype;
-
- for (;;) {
- char *w;
- size_t l;
- char *state;
- bool honour_argv0 = false, ignore = false;
-
- path = NULL;
- nce = NULL;
- n = NULL;
-
- rvalue += strspn(rvalue, WHITESPACE);
-
- if (rvalue[0] == 0)
- break;
-
- if (rvalue[0] == '-') {
- ignore = true;
- rvalue ++;
- }
-
- if (rvalue[0] == '@') {
- honour_argv0 = true;
- rvalue ++;
- }
-
- if (*rvalue != '/') {
- log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- k = 0;
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
- break;
-
- k++;
- }
-
- n = new(char*, k + !honour_argv0);
- if (!n)
- return -ENOMEM;
-
- k = 0;
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
- break;
-
- if (honour_argv0 && w == rvalue) {
- assert(!path);
-
- path = strndup(w, l);
- if (!path) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!utf8_is_valid(path)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- r = 0;
- goto fail;
- }
-
- } else {
- char *c;
-
- c = n[k++] = cunescape_length(w, l);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!utf8_is_valid(c)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
- r = 0;
- goto fail;
- }
- }
- }
-
- n[k] = NULL;
-
- if (!n[0]) {
- log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
- r = 0;
- goto fail;
- }
-
- if (!path) {
- path = strdup(n[0]);
- if (!path) {
- r = -ENOMEM;
- goto fail;
- }
- }
-
- assert(path_is_absolute(path));
-
- nce = new0(ExecCommand, 1);
- if (!nce) {
- r = -ENOMEM;
- goto fail;
- }
-
- nce->argv = n;
- nce->path = path;
- nce->ignore = ignore;
-
- path_kill_slashes(nce->path);
-
- exec_command_append_list(e, nce);
-
- rvalue = state;
- }
-
- return 0;
-
-fail:
- n[k] = NULL;
- strv_free(n);
- free(path);
- free(nce);
-
- return r;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
-
-int config_parse_socket_bindtodevice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Socket *s = data;
- char *n;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (rvalue[0] && !streq(rvalue, "*")) {
- if (!(n = strdup(rvalue)))
- return -ENOMEM;
- } else
- n = NULL;
-
- free(s->bind_to_device);
- s->bind_to_device = n;
-
- return 0;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
-
-int config_parse_facility(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
-
- int *o = data, x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((x = log_facility_unshifted_from_string(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *o = (x << 3) | LOG_PRI(*o);
-
- return 0;
-}
-
-int config_parse_level(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
-
- int *o = data, x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((x = log_level_from_string(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *o = (*o & LOG_FACMASK) | x;
- return 0;
-}
-
-int config_parse_exec_io_class(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- int x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((x = ioprio_class_from_string(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->ioprio = IOPRIO_PRIO_VALUE(x, IOPRIO_PRIO_DATA(c->ioprio));
- c->ioprio_set = true;
-
- return 0;
-}
-
-int config_parse_exec_io_priority(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- int i;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
- log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), i);
- c->ioprio_set = true;
-
- return 0;
-}
-
-int config_parse_exec_cpu_sched_policy(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
-
- ExecContext *c = data;
- int x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((x = sched_policy_from_string(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->cpu_sched_policy = x;
- c->cpu_sched_set = true;
-
- return 0;
-}
-
-int config_parse_exec_cpu_sched_prio(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- int i;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- /* On Linux RR/FIFO have the same range */
- if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
- log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->cpu_sched_priority = i;
- c->cpu_sched_set = true;
-
- return 0;
-}
-
-int config_parse_exec_cpu_affinity(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t;
- int r;
- unsigned cpu;
-
- if (!(t = strndup(w, l)))
- return -ENOMEM;
-
- r = safe_atou(t, &cpu);
- free(t);
-
- if (!(c->cpuset))
- if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus)))
- return -ENOMEM;
-
- if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
- }
-
- return 0;
-}
-
-int config_parse_exec_capabilities(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- cap_t cap;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (!(cap = cap_from_text(rvalue))) {
- if (errno == ENOMEM)
- return -ENOMEM;
-
- log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (c->capabilities)
- cap_free(c->capabilities);
- c->capabilities = cap;
-
- return 0;
-}
-
-int config_parse_exec_secure_bits(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (first_word(w, "keep-caps"))
- c->secure_bits |= SECURE_KEEP_CAPS;
- else if (first_word(w, "keep-caps-locked"))
- c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
- else if (first_word(w, "no-setuid-fixup"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP;
- else if (first_word(w, "no-setuid-fixup-locked"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
- else if (first_word(w, "noroot"))
- c->secure_bits |= SECURE_NOROOT;
- else if (first_word(w, "noroot-locked"))
- c->secure_bits |= SECURE_NOROOT_LOCKED;
- else {
- log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue);
- return 0;
- }
- }
-
- return 0;
-}
-
-int config_parse_exec_bounding_set(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- char *w;
- size_t l;
- char *state;
- bool invert = false;
- uint64_t sum = 0;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (rvalue[0] == '~') {
- invert = true;
- rvalue++;
- }
-
- /* Note that we store this inverted internally, since the
- * kernel wants it like this. But we actually expose it
- * non-inverted everywhere to have a fully normalized
- * interface. */
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t;
- int r;
- cap_value_t cap;
-
- if (!(t = strndup(w, l)))
- return -ENOMEM;
-
- r = cap_from_name(t, &cap);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
- }
-
- if (invert)
- c->capability_bounding_set_drop |= sum;
- else
- c->capability_bounding_set_drop |= ~sum;
-
- return 0;
-}
-
-int config_parse_exec_timer_slack_nsec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- unsigned long u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atolu(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse time slack value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c->timer_slack_nsec = u;
-
- return 0;
-}
-
-int config_parse_limit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- struct rlimit **rl = data;
- unsigned long long u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- rl += ltype;
-
- if (streq(rvalue, "infinity"))
- u = (unsigned long long) RLIM_INFINITY;
- else if (safe_atollu(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!*rl)
- if (!(*rl = new(struct rlimit, 1)))
- return -ENOMEM;
-
- (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
- return 0;
-}
-
-int config_parse_unit_cgroup(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *w;
- size_t l;
- char *state;
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
- int r;
-
- t = strndup(w, l);
- if (!t)
- return -ENOMEM;
-
- k = unit_full_printf(u, t);
- free(t);
-
- if (!k)
- return -ENOMEM;
-
- t = cunescape(k);
- free(k);
-
- if (!t)
- return -ENOMEM;
-
- r = unit_add_cgroup_from_text(u, t);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
- }
-
- return 0;
-}
-
-#ifdef HAVE_SYSV_COMPAT
-int config_parse_sysv_priority(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *priority = data;
- int i;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atoi(rvalue, &i) < 0 || i < 0) {
- log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *priority = (int) i;
- return 0;
-}
-#endif
-
-int config_parse_fsck_passno(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *passno = data;
- int i;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atoi(rvalue, &i) || i < 0) {
- log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *passno = (int) i;
- return 0;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
-
-int config_parse_kill_signal(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *sig = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(sig);
-
- if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
- log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *sig = r;
- return 0;
-}
-
-int config_parse_exec_mount_flags(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecContext *c = data;
- char *w;
- size_t l;
- char *state;
- unsigned long flags = 0;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, "shared", MAX(l, 6U)) == 0)
- flags |= MS_SHARED;
- else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
- flags |= MS_SLAVE;
- else if (strncmp(w, "private", MAX(l, 7U)) == 0)
- flags |= MS_PRIVATE;
- else {
- log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
- return 0;
- }
- }
-
- c->mount_flags = flags;
- return 0;
-}
-
-int config_parse_timer(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Timer *t = data;
- usec_t u;
- TimerValue *v;
- TimerBase b;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((b = timer_base_from_string(lvalue)) < 0) {
- log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
- return 0;
- }
-
- if (parse_usec(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!(v = new0(TimerValue, 1)))
- return -ENOMEM;
-
- v->base = b;
- v->value = u;
-
- LIST_PREPEND(TimerValue, value, t->values, v);
-
- return 0;
-}
-
-int config_parse_timer_unit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Timer *t = data;
- int r;
- DBusError error;
- Unit *u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- dbus_error_init(&error);
-
- if (endswith(rvalue, ".timer")) {
- log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
- if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
- return 0;
- }
-
- unit_ref_set(&t->unit, u);
-
- return 0;
-}
-
-int config_parse_path_spec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Path *p = data;
- PathSpec *s;
- PathType b;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((b = path_type_from_string(lvalue)) < 0) {
- log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
- return 0;
- }
-
- if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!(s = new0(PathSpec, 1)))
- return -ENOMEM;
-
- if (!(s->path = strdup(rvalue))) {
- free(s);
- return -ENOMEM;
- }
-
- path_kill_slashes(s->path);
-
- s->type = b;
- s->inotify_fd = -1;
-
- LIST_PREPEND(PathSpec, spec, p->specs, s);
-
- return 0;
-}
-
-int config_parse_path_unit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Path *t = data;
- int r;
- DBusError error;
- Unit *u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- dbus_error_init(&error);
-
- if (endswith(rvalue, ".path")) {
- log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
- return 0;
- }
-
- unit_ref_set(&t->unit, u);
-
- return 0;
-}
-
-int config_parse_socket_service(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Socket *s = data;
- int r;
- DBusError error;
- Unit *x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- dbus_error_init(&error);
-
- if (!endswith(rvalue, ".service")) {
- log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
- if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
- return 0;
- }
-
- unit_ref_set(&s->service, x);
-
- return 0;
-}
-
-int config_parse_service_sockets(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Service *s = data;
- int r;
- char *state, *w;
- size_t l;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
-
- t = strndup(w, l);
- if (!t)
- return -ENOMEM;
-
- k = unit_name_printf(UNIT(s), t);
- free(t);
-
- if (!k)
- return -ENOMEM;
-
- if (!endswith(k, ".socket")) {
- log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
- free(k);
- continue;
- }
-
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
- if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
-
- r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
- if (r < 0)
- return r;
-
- free(k);
- }
-
- return 0;
-}
-
-int config_parse_unit_env_file(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char ***env = data, **k;
- Unit *u = userdata;
- char *s;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- s = unit_full_printf(u, rvalue);
- if (!s)
- return -ENOMEM;
-
- if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
- log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
- free(s);
- return 0;
- }
-
- k = strv_append(*env, s);
- free(s);
- if (!k)
- return -ENOMEM;
-
- strv_free(*env);
- *env = k;
-
- return 0;
-}
-
-int config_parse_ip_tos(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *ip_tos = data, x;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((x = ip_tos_from_string(rvalue)) < 0)
- if (safe_atoi(rvalue, &x) < 0) {
- log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- *ip_tos = x;
- return 0;
-}
-
-int config_parse_unit_condition_path(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ConditionType cond = ltype;
- Unit *u = data;
- bool trigger, negate;
- Condition *c;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- trigger = rvalue[0] == '|';
- if (trigger)
- rvalue++;
-
- negate = rvalue[0] == '!';
- if (negate)
- rvalue++;
-
- if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- c = condition_new(cond, rvalue, trigger, negate);
- if (!c)
- return -ENOMEM;
-
- LIST_PREPEND(Condition, conditions, u->conditions, c);
- return 0;
-}
-
-int config_parse_unit_condition_string(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ConditionType cond = ltype;
- Unit *u = data;
- bool trigger, negate;
- Condition *c;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((trigger = rvalue[0] == '|'))
- rvalue++;
-
- if ((negate = rvalue[0] == '!'))
- rvalue++;
-
- if (!(c = condition_new(cond, rvalue, trigger, negate)))
- return -ENOMEM;
-
- LIST_PREPEND(Condition, conditions, u->conditions, c);
- return 0;
-}
-
-int config_parse_unit_condition_null(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = data;
- Condition *c;
- bool trigger, negate;
- int b;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if ((trigger = rvalue[0] == '|'))
- rvalue++;
-
- if ((negate = rvalue[0] == '!'))
- rvalue++;
-
- if ((b = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!b)
- negate = !negate;
-
- if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
- return -ENOMEM;
-
- LIST_PREPEND(Condition, conditions, u->conditions, c);
- return 0;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
-
-int config_parse_unit_cgroup_attr(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = data;
- char **l;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 2) {
- log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
- strv_free(l);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- unsigned long ul;
- char *t;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
- log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%lu", ul) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t sz;
- char *t;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
- log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u,
- "memory",
- streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
- t, NULL);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int device_map(const char *controller, const char *name, const char *value, char **ret) {
- char **l;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) >= 1);
-
- if (streq(l[0], "*")) {
-
- if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
-
- } else {
- struct stat st;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", l[0]);
- strv_free(l);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%c %u:%u%s%s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
-
- strv_free(l);
- return -ENOMEM;
- }
- }
-
- strv_free(l);
- return 0;
-}
-
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- char **l;
- int r;
- unsigned k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
- log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
- log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
- strv_free(l);
-
- r = unit_add_cgroup_attribute(u, "devices",
- streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
- rvalue, device_map);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
- struct stat st;
- char **l;
- dev_t d;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) == 2);
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (S_ISBLK(st.st_mode))
- d = st.st_rdev;
- else if (major(st.st_dev) != 0) {
- /* If this is not a device node then find the block
- * device this file is stored on */
- d = st.st_dev;
-
- /* If this is a partition, try to get the originating
- * block device */
- block_get_whole_disk(d, &d);
- } else {
- log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
- strv_free(l);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
-
- strv_free(l);
- return 0;
-}
-
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- unsigned long ul;
- const char *device = NULL, *weight;
- unsigned k;
- char *t, **l;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (k == 1)
- weight = l[0];
- else {
- device = l[0];
- weight = l[1];
- }
-
- if (device && !path_is_absolute(device)) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
- log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (device)
- r = asprintf(&t, "%s %lu", device, ul);
- else
- r = asprintf(&t, "%lu", ul);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- if (device)
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map);
- else
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t bytes;
- unsigned k;
- char *t, **l;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k != 2) {
- log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (!path_is_absolute(l[0])) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
- log_error("[%s:%u] Failed to parse block IO bandwith value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "blkio",
- streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
- t, blkio_map);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-
-#define FOLLOW_MAX 8
-
-static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
- unsigned c = 0;
- int fd, r;
- FILE *f;
- char *id = NULL;
-
- assert(filename);
- assert(*filename);
- assert(_f);
- assert(names);
-
- /* This will update the filename pointer if the loaded file is
- * reached by a symlink. The old string will be freed. */
-
- for (;;) {
- char *target, *name;
-
- if (c++ >= FOLLOW_MAX)
- return -ELOOP;
-
- path_kill_slashes(*filename);
-
- /* Add the file name we are currently looking at to
- * the names of this unit, but only if it is a valid
- * unit name. */
- name = file_name_from_path(*filename);
-
- if (unit_name_is_valid(name, true)) {
-
- id = set_get(names, name);
- if (!id) {
- id = strdup(name);
- if (!id)
- return -ENOMEM;
-
- r = set_put(names, id);
- if (r < 0) {
- free(id);
- return r;
- }
- }
- }
-
- /* Try to open the file name, but don't if its a symlink */
- if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
- break;
-
- if (errno != ELOOP)
- return -errno;
-
- /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
- if ((r = readlink_and_make_absolute(*filename, &target)) < 0)
- return r;
-
- free(*filename);
- *filename = target;
- }
-
- if (!(f = fdopen(fd, "re"))) {
- r = -errno;
- close_nointr_nofail(fd);
- return r;
- }
-
- *_f = f;
- *_final = id;
- return 0;
-}
-
-static int merge_by_names(Unit **u, Set *names, const char *id) {
- char *k;
- int r;
-
- assert(u);
- assert(*u);
- assert(names);
-
- /* Let's try to add in all symlink names we found */
- while ((k = set_steal_first(names))) {
-
- /* First try to merge in the other name into our
- * unit */
- if ((r = unit_merge_by_name(*u, k)) < 0) {
- Unit *other;
-
- /* Hmm, we couldn't merge the other unit into
- * ours? Then let's try it the other way
- * round */
-
- other = manager_get_unit((*u)->manager, k);
- free(k);
-
- if (other)
- if ((r = unit_merge(other, *u)) >= 0) {
- *u = other;
- return merge_by_names(u, names, NULL);
- }
-
- return r;
- }
-
- if (id == k)
- unit_choose_id(*u, id);
-
- free(k);
- }
-
- return 0;
-}
-
-static int load_from_path(Unit *u, const char *path) {
- int r;
- Set *symlink_names;
- FILE *f = NULL;
- char *filename = NULL, *id = NULL;
- Unit *merged;
- struct stat st;
-
- assert(u);
- assert(path);
-
- symlink_names = set_new(string_hash_func, string_compare_func);
- if (!symlink_names)
- return -ENOMEM;
-
- if (path_is_absolute(path)) {
-
- if (!(filename = strdup(path))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
- free(filename);
- filename = NULL;
-
- if (r != -ENOENT)
- goto finish;
- }
-
- } else {
- char **p;
-
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
-
- /* Instead of opening the path right away, we manually
- * follow all symlinks and add their name to our unit
- * name set while doing so */
- if (!(filename = path_make_absolute(path, *p))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (u->manager->unit_path_cache &&
- !set_get(u->manager->unit_path_cache, filename))
- r = -ENOENT;
- else
- r = open_follow(&filename, &f, symlink_names, &id);
-
- if (r < 0) {
- char *sn;
-
- free(filename);
- filename = NULL;
-
- if (r != -ENOENT)
- goto finish;
-
- /* Empty the symlink names for the next run */
- while ((sn = set_steal_first(symlink_names)))
- free(sn);
-
- continue;
- }
-
- break;
- }
- }
-
- if (!filename) {
- /* Hmm, no suitable file found? */
- r = 0;
- goto finish;
- }
-
- merged = u;
- if ((r = merge_by_names(&merged, symlink_names, id)) < 0)
- goto finish;
-
- if (merged != u) {
- u->load_state = UNIT_MERGED;
- r = 0;
- goto finish;
- }
-
- zero(st);
- if (fstat(fileno(f), &st) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (null_or_empty(&st))
- u->load_state = UNIT_MASKED;
- else {
- /* Now, parse the file contents */
- r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
- if (r < 0)
- goto finish;
-
- u->load_state = UNIT_LOADED;
- }
-
- free(u->fragment_path);
- u->fragment_path = filename;
- filename = NULL;
-
- u->fragment_mtime = timespec_load(&st.st_mtim);
-
- r = 0;
-
-finish:
- set_free_free(symlink_names);
- free(filename);
-
- if (f)
- fclose(f);
-
- return r;
-}
-
-int unit_load_fragment(Unit *u) {
- int r;
- Iterator i;
- const char *t;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
- assert(u->id);
-
- /* First, try to find the unit under its id. We always look
- * for unit files in the default directories, to make it easy
- * to override things by placing things in /etc/systemd/system */
- if ((r = load_from_path(u, u->id)) < 0)
- return r;
-
- /* Try to find an alias we can load this with */
- if (u->load_state == UNIT_STUB)
- SET_FOREACH(t, u->names, i) {
-
- if (t == u->id)
- continue;
-
- if ((r = load_from_path(u, t)) < 0)
- return r;
-
- if (u->load_state != UNIT_STUB)
- break;
- }
-
- /* And now, try looking for it under the suggested (originally linked) path */
- if (u->load_state == UNIT_STUB && u->fragment_path) {
-
- if ((r = load_from_path(u, u->fragment_path)) < 0)
- return r;
-
- if (u->load_state == UNIT_STUB) {
- /* Hmm, this didn't work? Then let's get rid
- * of the fragment path stored for us, so that
- * we don't point to an invalid location. */
- free(u->fragment_path);
- u->fragment_path = NULL;
- }
- }
-
- /* Look for a template */
- if (u->load_state == UNIT_STUB && u->instance) {
- char *k;
-
- if (!(k = unit_name_template(u->id)))
- return -ENOMEM;
-
- r = load_from_path(u, k);
- free(k);
-
- if (r < 0)
- return r;
-
- if (u->load_state == UNIT_STUB)
- SET_FOREACH(t, u->names, i) {
-
- if (t == u->id)
- continue;
-
- if (!(k = unit_name_template(t)))
- return -ENOMEM;
-
- r = load_from_path(u, k);
- free(k);
-
- if (r < 0)
- return r;
-
- if (u->load_state != UNIT_STUB)
- break;
- }
- }
-
- return 0;
-}
-
-void unit_dump_config_items(FILE *f) {
- static const struct {
- const ConfigParserCallback callback;
- const char *rvalue;
- } table[] = {
- { config_parse_int, "INTEGER" },
- { config_parse_unsigned, "UNSIGNED" },
- { config_parse_bytes_size, "SIZE" },
- { config_parse_bool, "BOOLEAN" },
- { config_parse_string, "STRING" },
- { config_parse_path, "PATH" },
- { config_parse_unit_path_printf, "PATH" },
- { config_parse_strv, "STRING [...]" },
- { config_parse_exec_nice, "NICE" },
- { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
- { config_parse_exec_io_class, "IOCLASS" },
- { config_parse_exec_io_priority, "IOPRIORITY" },
- { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
- { config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
- { config_parse_exec_cpu_affinity, "CPUAFFINITY" },
- { config_parse_mode, "MODE" },
- { config_parse_unit_env_file, "FILE" },
- { config_parse_output, "OUTPUT" },
- { config_parse_input, "INPUT" },
- { config_parse_facility, "FACILITY" },
- { config_parse_level, "LEVEL" },
- { config_parse_exec_capabilities, "CAPABILITIES" },
- { config_parse_exec_secure_bits, "SECUREBITS" },
- { config_parse_exec_bounding_set, "BOUNDINGSET" },
- { config_parse_exec_timer_slack_nsec, "TIMERSLACK" },
- { config_parse_limit, "LIMIT" },
- { config_parse_unit_cgroup, "CGROUP [...]" },
- { config_parse_unit_deps, "UNIT [...]" },
- { config_parse_unit_names, "UNIT [...]" },
- { config_parse_exec, "PATH [ARGUMENT [...]]" },
- { config_parse_service_type, "SERVICETYPE" },
- { config_parse_service_restart, "SERVICERESTART" },
-#ifdef HAVE_SYSV_COMPAT
- { config_parse_sysv_priority, "SYSVPRIORITY" },
-#else
- { config_parse_warn_compat, "NOTSUPPORTED" },
-#endif
- { config_parse_kill_mode, "KILLMODE" },
- { config_parse_kill_signal, "SIGNAL" },
- { config_parse_socket_listen, "SOCKET [...]" },
- { config_parse_socket_bind, "SOCKETBIND" },
- { config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
- { config_parse_usec, "SECONDS" },
- { config_parse_path_strv, "PATH [...]" },
- { config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
- { config_parse_unit_string_printf, "STRING" },
- { config_parse_timer, "TIMER" },
- { config_parse_timer_unit, "NAME" },
- { config_parse_path_spec, "PATH" },
- { config_parse_path_unit, "UNIT" },
- { config_parse_notify_access, "ACCESS" },
- { config_parse_ip_tos, "TOS" },
- { config_parse_unit_condition_path, "CONDITION" },
- { config_parse_unit_condition_string, "CONDITION" },
- { config_parse_unit_condition_null, "CONDITION" },
- };
-
- const char *prev = NULL;
- const char *i;
-
- assert(f);
-
- NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
- const char *rvalue = "OTHER", *lvalue;
- unsigned j;
- size_t prefix_len;
- const char *dot;
- const ConfigPerfItem *p;
-
- assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
-
- dot = strchr(i, '.');
- lvalue = dot ? dot + 1 : i;
- prefix_len = dot-i;
-
- if (dot)
- if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
- if (prev)
- fputc('\n', f);
-
- fprintf(f, "[%.*s]\n", (int) prefix_len, i);
- }
-
- for (j = 0; j < ELEMENTSOF(table); j++)
- if (p->parse == table[j].callback) {
- rvalue = table[j].rvalue;
- break;
- }
-
- fprintf(f, "%s=%s\n", lvalue, rvalue);
- prev = i;
- }
-}
diff --git a/src/load-fragment.h b/src/load-fragment.h
deleted file mode 100644
index 79fc76d..0000000
--- a/src/load-fragment.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooloadfragmenthfoo
-#define fooloadfragmenthfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "unit.h"
-
-/* Read service data from .desktop file style configuration fragments */
-
-int unit_load_fragment(Unit *u);
-
-void unit_dump_config_items(FILE *f);
-
-int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_names(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_timer_slack_nsec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-/* gperf prototypes */
-const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
-extern const char load_fragment_gperf_nulstr[];
-
-#endif
diff --git a/src/locale-setup.c b/src/locale-setup.c
deleted file mode 100644
index 7f692e9..0000000
--- a/src/locale-setup.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "locale-setup.h"
-#include "util.h"
-#include "macro.h"
-#include "virt.h"
-
-enum {
- /* We don't list LC_ALL here on purpose. People should be
- * using LANG instead. */
-
- VARIABLE_LANG,
- VARIABLE_LANGUAGE,
- VARIABLE_LC_CTYPE,
- VARIABLE_LC_NUMERIC,
- VARIABLE_LC_TIME,
- VARIABLE_LC_COLLATE,
- VARIABLE_LC_MONETARY,
- VARIABLE_LC_MESSAGES,
- VARIABLE_LC_PAPER,
- VARIABLE_LC_NAME,
- VARIABLE_LC_ADDRESS,
- VARIABLE_LC_TELEPHONE,
- VARIABLE_LC_MEASUREMENT,
- VARIABLE_LC_IDENTIFICATION,
- _VARIABLE_MAX
-};
-
-static const char * const variable_names[_VARIABLE_MAX] = {
- [VARIABLE_LANG] = "LANG",
- [VARIABLE_LANGUAGE] = "LANGUAGE",
- [VARIABLE_LC_CTYPE] = "LC_CTYPE",
- [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
- [VARIABLE_LC_TIME] = "LC_TIME",
- [VARIABLE_LC_COLLATE] = "LC_COLLATE",
- [VARIABLE_LC_MONETARY] = "LC_MONETARY",
- [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
- [VARIABLE_LC_PAPER] = "LC_PAPER",
- [VARIABLE_LC_NAME] = "LC_NAME",
- [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
- [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
- [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
- [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
-};
-
-int locale_setup(void) {
- char *variables[_VARIABLE_MAX];
- int r = 0, i;
-
- zero(variables);
-
- if (detect_container(NULL) <= 0)
- if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
-#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
- "LANG", &variables[VARIABLE_LANG],
-#endif
- "locale.LANG", &variables[VARIABLE_LANG],
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
- }
-
- /* Hmm, nothing set on the kernel cmd line? Then let's
- * try /etc/locale.conf */
- if (r <= 0 &&
- (r = parse_env_file("/etc/locale.conf", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/locale.conf: %s", strerror(-r));
- }
-
-#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MEEGO)
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
- }
-
-#elif defined(TARGET_SUSE)
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/language", NEWLINE,
- "RC_LANG", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r));
- }
-
-#elif defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (r <= 0 &&
- (r = parse_env_file("/etc/default/locale", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/default/locale: %s", strerror(-r));
- }
-
-#elif defined(TARGET_ARCH)
- if (r <= 0 &&
- (r = parse_env_file("/etc/rc.conf", NEWLINE,
- "LOCALE", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
- }
-
-#elif defined(TARGET_GENTOO)
- /* Gentoo's openrc expects locale variables in /etc/env.d/
- * These files are later compiled by env-update into shell
- * export commands at /etc/profile.env, with variables being
- * exported by openrc's runscript (so /etc/init.d/)
- */
- if (r <= 0 &&
- (r = parse_env_file("/etc/profile.env", NEWLINE,
- "export LANG", &variables[VARIABLE_LANG],
- "export LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "export LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "export LC_TIME", &variables[VARIABLE_LC_TIME],
- "export LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "export LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "export LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "export LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "export LC_NAME", &variables[VARIABLE_LC_NAME],
- "export LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "export LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "export LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/profile.env: %s", strerror(-r));
- }
-#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA )
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
- }
-
-#endif
-
- if (!variables[VARIABLE_LANG]) {
- if (!(variables[VARIABLE_LANG] = strdup("C"))) {
- r = -ENOMEM;
- goto finish;
- }
- }
-
- for (i = 0; i < _VARIABLE_MAX; i++) {
-
- if (variables[i]) {
- if (setenv(variable_names[i], variables[i], 1) < 0) {
- r = -errno;
- goto finish;
- }
- } else
- unsetenv(variable_names[i]);
- }
-
- r = 0;
-
-finish:
- for (i = 0; i < _VARIABLE_MAX; i++)
- free(variables[i]);
-
- return r;
-}
diff --git a/src/locale-setup.h b/src/locale-setup.h
deleted file mode 100644
index 09a6bc6..0000000
--- a/src/locale-setup.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foolocalesetuphfoo
-#define foolocalesetuphfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int locale_setup(void);
-
-#endif
diff --git a/src/manager.c b/src/manager.c
deleted file mode 100644
index 312527a..0000000
--- a/src/manager.c
+++ /dev/null
@@ -1,3235 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <signal.h>
-#include <sys/signalfd.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <sys/reboot.h>
-#include <sys/ioctl.h>
-#include <linux/kd.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-
-#ifdef HAVE_AUDIT
-#include <libaudit.h>
-#endif
-
-#include <systemd/sd-daemon.h>
-
-#include "manager.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "ratelimit.h"
-#include "cgroup.h"
-#include "mount-setup.h"
-#include "unit-name.h"
-#include "dbus-unit.h"
-#include "dbus-job.h"
-#include "missing.h"
-#include "path-lookup.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "exit-status.h"
-#include "virt.h"
-#include "watchdog.h"
-
-/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
-#define GC_QUEUE_ENTRIES_MAX 16
-
-/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
-#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
-
-/* Where clients shall send notification messages to */
-#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
-#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
-
-static int manager_setup_notify(Manager *m) {
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- } sa;
- struct epoll_event ev;
- int one = 1, r;
- mode_t u;
-
- assert(m);
-
- m->notify_watch.type = WATCH_NOTIFY;
- if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
- log_error("Failed to allocate notification socket: %m");
- return -errno;
- }
-
- zero(sa);
- sa.sa.sa_family = AF_UNIX;
-
- if (getpid() != 1)
- snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
- else {
- unlink(NOTIFY_SOCKET_SYSTEM);
- strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
- }
-
- if (sa.un.sun_path[0] == '@')
- sa.un.sun_path[0] = 0;
-
- u = umask(0111);
- r = bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
- umask(u);
-
- if (r < 0) {
- log_error("bind() failed: %m");
- return -errno;
- }
-
- if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
- log_error("SO_PASSCRED failed: %m");
- return -errno;
- }
-
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->notify_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
- return -errno;
-
- if (sa.un.sun_path[0] == 0)
- sa.un.sun_path[0] = '@';
-
- if (!(m->notify_socket = strdup(sa.un.sun_path)))
- return -ENOMEM;
-
- log_debug("Using notification socket %s", m->notify_socket);
-
- return 0;
-}
-
-static int enable_special_signals(Manager *m) {
- int fd;
-
- assert(m);
-
- /* Enable that we get SIGINT on control-alt-del */
- if (reboot(RB_DISABLE_CAD) < 0)
- log_warning("Failed to enable ctrl-alt-del handling: %m");
-
- if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
- log_warning("Failed to open /dev/tty0: %m");
- else {
- /* Enable that we get SIGWINCH on kbrequest */
- if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
- log_warning("Failed to enable kbrequest handling: %s", strerror(errno));
-
- close_nointr_nofail(fd);
- }
-
- return 0;
-}
-
-static int manager_setup_signals(Manager *m) {
- sigset_t mask;
- struct epoll_event ev;
- struct sigaction sa;
-
- assert(m);
-
- /* We are not interested in SIGSTOP and friends. */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
- assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
-
- assert_se(sigemptyset(&mask) == 0);
-
- 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 */
- SIGRTMIN+13, /* systemd: Immediate halt */
- SIGRTMIN+14, /* systemd: Immediate poweroff */
- SIGRTMIN+15, /* systemd: Immediate reboot */
- SIGRTMIN+16, /* systemd: Immediate kexec */
- 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+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 */
- -1);
- assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
- m->signal_watch.type = WATCH_SIGNAL;
- if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
- return -errno;
-
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->signal_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
- return -errno;
-
- if (m->running_as == MANAGER_SYSTEM)
- return enable_special_signals(m);
-
- return 0;
-}
-
-static void manager_strip_environment(Manager *m) {
- assert(m);
-
- /* Remove variables from the inherited set that are part of
- * the container interface:
- * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
- strv_remove_prefix(m->environment, "container=");
- strv_remove_prefix(m->environment, "container_");
-
- /* Remove variables from the inherited set that are part of
- * the initrd interface:
- * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
- strv_remove_prefix(m->environment, "RD_");
-}
-
-int manager_new(ManagerRunningAs running_as, Manager **_m) {
- Manager *m;
- int r = -ENOMEM;
-
- assert(_m);
- assert(running_as >= 0);
- assert(running_as < _MANAGER_RUNNING_AS_MAX);
-
- if (!(m = new0(Manager, 1)))
- return -ENOMEM;
-
- dual_timestamp_get(&m->startup_timestamp);
-
- m->running_as = running_as;
- m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
- m->exit_code = _MANAGER_EXIT_CODE_INVALID;
- m->pin_cgroupfs_fd = -1;
-
-#ifdef HAVE_AUDIT
- m->audit_fd = -1;
-#endif
-
- m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
- m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
-
- m->environment = strv_copy(environ);
- if (!m->environment)
- goto fail;
-
- manager_strip_environment(m);
-
- if (running_as == MANAGER_SYSTEM) {
- m->default_controllers = strv_new("cpu", NULL);
- if (!m->default_controllers)
- goto fail;
- }
-
- if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
- goto fail;
-
- if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
- goto fail;
-
- if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
- goto fail;
-
- if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
- goto fail;
-
- if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
- goto fail;
-
- if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
- goto fail;
-
- if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
- goto fail;
-
- if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
- goto fail;
-
- if ((r = manager_setup_signals(m)) < 0)
- goto fail;
-
- if ((r = manager_setup_cgroup(m)) < 0)
- goto fail;
-
- if ((r = manager_setup_notify(m)) < 0)
- goto fail;
-
- /* Try to connect to the busses, if possible. */
- if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0)
- goto fail;
-
-#ifdef HAVE_AUDIT
- if ((m->audit_fd = audit_open()) < 0 &&
- /* If the kernel lacks netlink or audit support,
- * don't worry about it. */
- errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
- log_error("Failed to connect to audit log: %m");
-#endif
-
- m->taint_usr = dir_is_empty("/usr") > 0;
-
- *_m = m;
- return 0;
-
-fail:
- manager_free(m);
- return r;
-}
-
-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) {
- Job *j;
- Unit *u;
-
- assert(m);
-
- while ((j = hashmap_first(m->transaction_jobs)))
- job_free(j);
-
- 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->transaction_jobs));
- 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->transaction_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);
-
- 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;
-}
-
-static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
- assert(m);
- assert(j);
-
- /* Deletes one job from the transaction */
-
- manager_transaction_unlink_job(m, j, delete_dependencies);
-
- if (!j->installed)
- job_free(j);
-}
-
-static void transaction_delete_unit(Manager *m, Unit *u) {
- Job *j;
-
- /* Deletes all jobs associated with a certain unit from the
- * transaction */
-
- while ((j = hashmap_get(m->transaction_jobs, u)))
- transaction_delete_job(m, j, true);
-}
-
-static void transaction_clean_dependencies(Manager *m) {
- Iterator i;
- Job *j;
-
- assert(m);
-
- /* Drops all dependencies of all installed jobs */
-
- HASHMAP_FOREACH(j, m->jobs, i) {
- while (j->subject_list)
- job_dependency_free(j->subject_list);
- while (j->object_list)
- job_dependency_free(j->object_list);
- }
-
- assert(!m->transaction_anchor);
-}
-
-static void transaction_abort(Manager *m) {
- Job *j;
-
- assert(m);
-
- while ((j = hashmap_first(m->transaction_jobs)))
- if (j->installed)
- transaction_delete_job(m, j, true);
- else
- job_free(j);
-
- assert(hashmap_isempty(m->transaction_jobs));
-
- transaction_clean_dependencies(m);
-}
-
-static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) {
- JobDependency *l;
-
- assert(m);
-
- /* A recursive sweep through the graph that marks all units
- * that matter to the anchor job, i.e. are directly or
- * indirectly a dependency of the anchor job via paths that
- * are fully marked as mattering. */
-
- if (j)
- l = j->subject_list;
- else
- l = m->transaction_anchor;
-
- LIST_FOREACH(subject, l, l) {
-
- /* This link does not matter */
- if (!l->matters)
- continue;
-
- /* This unit has already been marked */
- if (l->object->generation == generation)
- continue;
-
- l->object->matters_to_anchor = true;
- l->object->generation = generation;
-
- transaction_find_jobs_that_matter_to_anchor(m, l->object, generation);
- }
-}
-
-static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) {
- JobDependency *l, *last;
-
- assert(j);
- assert(other);
- assert(j->unit == other->unit);
- assert(!j->installed);
-
- /* Merges 'other' into 'j' and then deletes j. */
-
- j->type = t;
- j->state = JOB_WAITING;
- j->override = j->override || other->override;
-
- j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
-
- /* Patch us in as new owner of the JobDependency objects */
- last = NULL;
- LIST_FOREACH(subject, l, other->subject_list) {
- assert(l->subject == other);
- l->subject = j;
- last = l;
- }
-
- /* Merge both lists */
- if (last) {
- last->subject_next = j->subject_list;
- if (j->subject_list)
- j->subject_list->subject_prev = last;
- j->subject_list = other->subject_list;
- }
-
- /* Patch us in as new owner of the JobDependency objects */
- last = NULL;
- LIST_FOREACH(object, l, other->object_list) {
- assert(l->object == other);
- l->object = j;
- last = l;
- }
-
- /* Merge both lists */
- if (last) {
- last->object_next = j->object_list;
- if (j->object_list)
- j->object_list->object_prev = last;
- j->object_list = other->object_list;
- }
-
- /* Kill the other job */
- other->subject_list = NULL;
- other->object_list = NULL;
- transaction_delete_job(m, other, true);
-}
-static bool job_is_conflicted_by(Job *j) {
- JobDependency *l;
-
- assert(j);
-
- /* Returns true if this job is pulled in by a least one
- * ConflictedBy dependency. */
-
- LIST_FOREACH(object, l, j->object_list)
- if (l->conflicts)
- return true;
-
- return false;
-}
-
-static int delete_one_unmergeable_job(Manager *m, Job *j) {
- Job *k;
-
- assert(j);
-
- /* Tries to delete one item in the linked list
- * j->transaction_next->transaction_next->... that conflicts
- * with another one, in an attempt to make an inconsistent
- * transaction work. */
-
- /* We rely here on the fact that if a merged with b does not
- * merge with c, either a or b merge with c neither */
- LIST_FOREACH(transaction, j, j)
- LIST_FOREACH(transaction, k, j->transaction_next) {
- Job *d;
-
- /* Is this one mergeable? Then skip it */
- if (job_type_is_mergeable(j->type, k->type))
- continue;
-
- /* Ok, we found two that conflict, let's see if we can
- * drop one of them */
- if (!j->matters_to_anchor && !k->matters_to_anchor) {
-
- /* Both jobs don't matter, so let's
- * find the one that is smarter to
- * remove. Let's think positive and
- * rather remove stops then starts --
- * except if something is being
- * stopped because it is conflicted by
- * another unit in which case we
- * rather remove the start. */
-
- log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
- log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
-
- if (j->type == JOB_STOP) {
-
- if (job_is_conflicted_by(j))
- d = k;
- else
- d = j;
-
- } else if (k->type == JOB_STOP) {
-
- if (job_is_conflicted_by(k))
- d = j;
- else
- d = k;
- } else
- d = j;
-
- } else if (!j->matters_to_anchor)
- d = j;
- else if (!k->matters_to_anchor)
- d = k;
- else
- return -ENOEXEC;
-
- /* Ok, we can drop one, so let's do so. */
- log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
- transaction_delete_job(m, d, true);
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int transaction_merge_jobs(Manager *m, DBusError *e) {
- Job *j;
- Iterator i;
- int r;
-
- assert(m);
-
- /* First step, check whether any of the jobs for one specific
- * task conflict. If so, try to drop one of them. */
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- JobType t;
- Job *k;
-
- t = j->type;
- LIST_FOREACH(transaction, k, j->transaction_next) {
- if (job_type_merge(&t, k->type) >= 0)
- continue;
-
- /* OK, we could not merge all jobs for this
- * action. Let's see if we can get rid of one
- * of them */
-
- if ((r = delete_one_unmergeable_job(m, j)) >= 0)
- /* Ok, we managed to drop one, now
- * let's ask our callers to call us
- * again after garbage collecting */
- return -EAGAIN;
-
- /* We couldn't merge anything. Failure */
- dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
- job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
- return r;
- }
- }
-
- /* Second step, merge the jobs. */
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- JobType t = j->type;
- Job *k;
-
- /* Merge all transactions */
- LIST_FOREACH(transaction, k, j->transaction_next)
- assert_se(job_type_merge(&t, k->type) == 0);
-
- /* If an active job is mergeable, merge it too */
- if (j->unit->job)
- job_type_merge(&t, j->unit->job->type); /* Might fail. Which is OK */
-
- while ((k = j->transaction_next)) {
- if (j->installed) {
- transaction_merge_and_delete_job(m, k, j, t);
- j = k;
- } else
- transaction_merge_and_delete_job(m, j, k, t);
- }
-
- if (j->unit->job && !j->installed)
- transaction_merge_and_delete_job(m, j, j->unit->job, t);
-
- assert(!j->transaction_next);
- assert(!j->transaction_prev);
- }
-
- return 0;
-}
-
-static void transaction_drop_redundant(Manager *m) {
- bool again;
-
- assert(m);
-
- /* Goes through the transaction and removes all jobs that are
- * a noop */
-
- do {
- Job *j;
- Iterator i;
-
- again = false;
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- bool changes_something = false;
- Job *k;
-
- LIST_FOREACH(transaction, k, j) {
-
- if (!job_is_anchor(k) &&
- (k->installed || job_type_is_redundant(k->type, unit_active_state(k->unit))) &&
- (!k->unit->job || !job_type_is_conflicting(k->type, k->unit->job->type)))
- continue;
-
- changes_something = true;
- break;
- }
-
- if (changes_something)
- continue;
-
- /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
- transaction_delete_job(m, j, false);
- again = true;
- break;
- }
-
- } while (again);
-}
-
-static bool unit_matters_to_anchor(Unit *u, Job *j) {
- assert(u);
- assert(!j->transaction_prev);
-
- /* Checks whether at least one of the jobs for this unit
- * matters to the anchor. */
-
- LIST_FOREACH(transaction, j, j)
- if (j->matters_to_anchor)
- return true;
-
- return false;
-}
-
-static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation, DBusError *e) {
- Iterator i;
- Unit *u;
- int r;
-
- assert(m);
- assert(j);
- assert(!j->transaction_prev);
-
- /* Does a recursive sweep through the ordering graph, looking
- * for a cycle. If we find cycle we try to break it. */
-
- /* Have we seen this before? */
- if (j->generation == generation) {
- Job *k, *delete;
-
- /* If the marker is NULL we have been here already and
- * decided the job was loop-free from here. Hence
- * shortcut things and return right-away. */
- if (!j->marker)
- return 0;
-
- /* So, the marker is not NULL and we already have been
- * here. We have a cycle. Let's try to break it. We go
- * backwards in our path and try to find a suitable
- * job to remove. We use the marker to find our way
- * back, since smart how we are we stored our way back
- * in there. */
- log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
-
- delete = NULL;
- for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
-
- log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
-
- if (!delete &&
- !k->installed &&
- !unit_matters_to_anchor(k->unit, k)) {
- /* Ok, we can drop this one, so let's
- * do so. */
- delete = k;
- }
-
- /* Check if this in fact was the beginning of
- * the cycle */
- if (k == j)
- break;
- }
-
-
- if (delete) {
- log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
- transaction_delete_unit(m, delete->unit);
- return -EAGAIN;
- }
-
- log_error("Unable to break cycle");
-
- dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
- return -ENOEXEC;
- }
-
- /* Make the marker point to where we come from, so that we can
- * find our way backwards if we want to break a cycle. We use
- * a special marker for the beginning: we point to
- * ourselves. */
- j->marker = from ? from : j;
- j->generation = generation;
-
- /* We assume that the the dependencies are bidirectional, and
- * hence can ignore UNIT_AFTER */
- SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
- Job *o;
-
- /* Is there a job for this unit? */
- if (!(o = hashmap_get(m->transaction_jobs, u)))
-
- /* Ok, there is no job for this in the
- * transaction, but maybe there is already one
- * running? */
- if (!(o = u->job))
- continue;
-
- if ((r = transaction_verify_order_one(m, o, j, generation, e)) < 0)
- return r;
- }
-
- /* Ok, let's backtrack, and remember that this entry is not on
- * our path anymore. */
- j->marker = NULL;
-
- return 0;
-}
-
-static int transaction_verify_order(Manager *m, unsigned *generation, DBusError *e) {
- Job *j;
- int r;
- Iterator i;
- unsigned g;
-
- assert(m);
- assert(generation);
-
- /* Check if the ordering graph is cyclic. If it is, try to fix
- * that up by dropping one of the jobs. */
-
- g = (*generation)++;
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i)
- if ((r = transaction_verify_order_one(m, j, NULL, g, e)) < 0)
- return r;
-
- return 0;
-}
-
-static void transaction_collect_garbage(Manager *m) {
- bool again;
-
- assert(m);
-
- /* Drop jobs that are not required by any other job */
-
- do {
- Iterator i;
- Job *j;
-
- again = false;
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- if (j->object_list) {
- /* log_debug("Keeping job %s/%s because of %s/%s", */
- /* j->unit->id, job_type_to_string(j->type), */
- /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
- /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
- continue;
- }
-
- /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
- transaction_delete_job(m, j, true);
- again = true;
- break;
- }
-
- } while (again);
-}
-
-static int transaction_is_destructive(Manager *m, DBusError *e) {
- Iterator i;
- Job *j;
-
- assert(m);
-
- /* Checks whether applying this transaction means that
- * existing jobs would be replaced */
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
-
- /* Assume merged */
- assert(!j->transaction_prev);
- assert(!j->transaction_next);
-
- if (j->unit->job &&
- j->unit->job != j &&
- !job_type_is_superset(j->type, j->unit->job->type)) {
-
- dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
- return -EEXIST;
- }
- }
-
- return 0;
-}
-
-static void transaction_minimize_impact(Manager *m) {
- bool again;
- assert(m);
-
- /* Drops all unnecessary jobs that reverse already active jobs
- * or that stop a running service. */
-
- do {
- Job *j;
- Iterator i;
-
- again = false;
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- LIST_FOREACH(transaction, j, j) {
- bool stops_running_service, changes_existing_job;
-
- /* If it matters, we shouldn't drop it */
- if (j->matters_to_anchor)
- continue;
-
- /* Would this stop a running service?
- * Would this change an existing job?
- * If so, let's drop this entry */
-
- stops_running_service =
- j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
-
- changes_existing_job =
- j->unit->job &&
- job_type_is_conflicting(j->type, j->unit->job->type);
-
- if (!stops_running_service && !changes_existing_job)
- continue;
-
- if (stops_running_service)
- log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
-
- if (changes_existing_job)
- log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
-
- /* Ok, let's get rid of this */
- log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
-
- transaction_delete_job(m, j, true);
- again = true;
- break;
- }
-
- if (again)
- break;
- }
-
- } while (again);
-}
-
-static int transaction_apply(Manager *m, JobMode mode) {
- Iterator i;
- Job *j;
- int r;
-
- /* Moves the transaction jobs to the set of active jobs */
-
- if (mode == JOB_ISOLATE) {
-
- /* When isolating first kill all installed jobs which
- * aren't part of the new transaction */
- rescan:
- HASHMAP_FOREACH(j, m->jobs, i) {
- assert(j->installed);
-
- if (hashmap_get(m->transaction_jobs, j->unit))
- continue;
-
- /* 'j' itself is safe to remove, but if other jobs
- are invalidated recursively, our iterator may become
- invalid and we need to start over. */
- if (job_finish_and_invalidate(j, JOB_CANCELED) > 0)
- goto rescan;
- }
- }
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- /* Assume merged */
- assert(!j->transaction_prev);
- assert(!j->transaction_next);
-
- if (j->installed)
- continue;
-
- if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0)
- goto rollback;
- }
-
- while ((j = hashmap_steal_first(m->transaction_jobs))) {
- if (j->installed) {
- /* log_debug("Skipping already installed job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); */
- continue;
- }
-
- if (j->unit->job)
- job_free(j->unit->job);
-
- j->unit->job = j;
- j->installed = true;
- m->n_installed_jobs ++;
-
- /* We're fully installed. Now let's free data we don't
- * need anymore. */
-
- assert(!j->transaction_next);
- assert(!j->transaction_prev);
-
- job_add_to_run_queue(j);
- job_add_to_dbus_queue(j);
- job_start_timer(j);
-
- log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
- }
-
- /* As last step, kill all remaining job dependencies. */
- transaction_clean_dependencies(m);
-
- return 0;
-
-rollback:
-
- HASHMAP_FOREACH(j, m->transaction_jobs, i) {
- if (j->installed)
- continue;
-
- hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
- }
-
- return r;
-}
-
-static int transaction_activate(Manager *m, JobMode mode, DBusError *e) {
- int r;
- unsigned generation = 1;
-
- assert(m);
-
- /* This applies the changes recorded in transaction_jobs to
- * the actual list of jobs, if possible. */
-
- /* First step: figure out which jobs matter */
- transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++);
-
- /* Second step: Try not to stop any running services if
- * we don't have to. Don't try to reverse running
- * jobs if we don't have to. */
- if (mode == JOB_FAIL)
- transaction_minimize_impact(m);
-
- /* Third step: Drop redundant jobs */
- transaction_drop_redundant(m);
-
- for (;;) {
- /* Fourth step: Let's remove unneeded jobs that might
- * be lurking. */
- if (mode != JOB_ISOLATE)
- transaction_collect_garbage(m);
-
- /* Fifth step: verify order makes sense and correct
- * cycles if necessary and possible */
- if ((r = transaction_verify_order(m, &generation, e)) >= 0)
- break;
-
- if (r != -EAGAIN) {
- log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
- goto rollback;
- }
-
- /* Let's see if the resulting transaction ordering
- * graph is still cyclic... */
- }
-
- for (;;) {
- /* Sixth step: let's drop unmergeable entries if
- * necessary and possible, merge entries we can
- * merge */
- if ((r = transaction_merge_jobs(m, e)) >= 0)
- break;
-
- if (r != -EAGAIN) {
- log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
- goto rollback;
- }
-
- /* Seventh step: an entry got dropped, let's garbage
- * collect its dependencies. */
- if (mode != JOB_ISOLATE)
- transaction_collect_garbage(m);
-
- /* Let's see if the resulting transaction still has
- * unmergeable entries ... */
- }
-
- /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
- transaction_drop_redundant(m);
-
- /* Ninth step: check whether we can actually apply this */
- if (mode == JOB_FAIL)
- if ((r = transaction_is_destructive(m, e)) < 0) {
- log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
- goto rollback;
- }
-
- /* Tenth step: apply changes */
- if ((r = transaction_apply(m, mode)) < 0) {
- log_warning("Failed to apply transaction: %s", strerror(-r));
- goto rollback;
- }
-
- assert(hashmap_isempty(m->transaction_jobs));
- assert(!m->transaction_anchor);
-
- return 0;
-
-rollback:
- transaction_abort(m);
- return r;
-}
-
-static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool override, bool *is_new) {
- Job *j, *f;
-
- assert(m);
- assert(unit);
-
- /* Looks for an existing prospective job and returns that. If
- * it doesn't exist it is created and added to the prospective
- * jobs list. */
-
- f = hashmap_get(m->transaction_jobs, unit);
-
- LIST_FOREACH(transaction, j, f) {
- assert(j->unit == unit);
-
- if (j->type == type) {
- if (is_new)
- *is_new = false;
- return j;
- }
- }
-
- if (unit->job && unit->job->type == type)
- j = unit->job;
- else if (!(j = job_new(m, type, unit)))
- return NULL;
-
- j->generation = 0;
- j->marker = NULL;
- j->matters_to_anchor = false;
- j->override = override;
-
- LIST_PREPEND(Job, transaction, f, j);
-
- if (hashmap_replace(m->transaction_jobs, unit, f) < 0) {
- job_free(j);
- return NULL;
- }
-
- if (is_new)
- *is_new = true;
-
- /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
-
- return j;
-}
-
-void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
- assert(m);
- assert(j);
-
- if (j->transaction_prev)
- j->transaction_prev->transaction_next = j->transaction_next;
- else if (j->transaction_next)
- hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next);
- else
- hashmap_remove_value(m->transaction_jobs, j->unit, j);
-
- if (j->transaction_next)
- j->transaction_next->transaction_prev = j->transaction_prev;
-
- j->transaction_prev = j->transaction_next = NULL;
-
- while (j->subject_list)
- job_dependency_free(j->subject_list);
-
- while (j->object_list) {
- Job *other = j->object_list->matters ? j->object_list->subject : NULL;
-
- job_dependency_free(j->object_list);
-
- if (other && delete_dependencies) {
- log_debug("Deleting job %s/%s as dependency of job %s/%s",
- other->unit->id, job_type_to_string(other->type),
- j->unit->id, job_type_to_string(j->type));
- transaction_delete_job(m, other, delete_dependencies);
- }
- }
-}
-
-static int transaction_add_job_and_dependencies(
- Manager *m,
- JobType type,
- Unit *unit,
- Job *by,
- bool matters,
- bool override,
- bool conflicts,
- bool ignore_requirements,
- bool ignore_order,
- DBusError *e,
- Job **_ret) {
- Job *ret;
- Iterator i;
- Unit *dep;
- int r;
- bool is_new;
-
- assert(m);
- assert(type < _JOB_TYPE_MAX);
- assert(unit);
-
- /* log_debug("Pulling in %s/%s from %s/%s", */
- /* unit->id, job_type_to_string(type), */
- /* by ? by->unit->id : "NA", */
- /* by ? job_type_to_string(by->type) : "NA"); */
-
- if (unit->load_state != UNIT_LOADED &&
- unit->load_state != UNIT_ERROR &&
- unit->load_state != UNIT_MASKED) {
- dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
- return -EINVAL;
- }
-
- if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
- dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s. "
- "See system logs and 'systemctl status %s' for details.",
- unit->id,
- strerror(-unit->load_error),
- unit->id);
- return -EINVAL;
- }
-
- if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
- dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
- return -EINVAL;
- }
-
- if (!unit_job_is_applicable(unit, type)) {
- dbus_set_error(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, "Job type %s is not applicable for unit %s.", job_type_to_string(type), unit->id);
- return -EBADR;
- }
-
- /* First add the job. */
- if (!(ret = transaction_add_one_job(m, type, unit, override, &is_new)))
- return -ENOMEM;
-
- ret->ignore_order = ret->ignore_order || ignore_order;
-
- /* Then, add a link to the job. */
- if (!job_dependency_new(by, ret, matters, conflicts))
- return -ENOMEM;
-
- if (is_new && !ignore_requirements) {
- Set *following;
-
- /* If we are following some other unit, make sure we
- * add all dependencies of everybody following. */
- if (unit_following_set(ret->unit, &following) > 0) {
- SET_FOREACH(dep, following, i)
- if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
-
- set_free(following);
- }
-
- /* Finally, recursively add in all dependencies. */
- if (type == JOB_START || type == JOB_RELOAD_OR_START) {
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_BIND_TO], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
-
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, false, ignore_order, e, NULL)) < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
-
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e, NULL)) < 0) {
-
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i)
- if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
-
- }
-
- if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i)
- if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
-
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i)
- if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
-
- if (r != -EBADR)
- goto fail;
-
- if (e)
- dbus_error_free(e);
- }
- }
-
- if (type == JOB_RELOAD || type == JOB_RELOAD_OR_START) {
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATE_RELOAD_TO], i) {
- r = transaction_add_job_and_dependencies(m, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e, NULL);
-
- if (r < 0) {
- log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
-
- if (e)
- dbus_error_free(e);
- }
- }
- }
-
- /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
- }
-
- if (_ret)
- *_ret = ret;
-
- return 0;
-
-fail:
- return r;
-}
-
-static int transaction_add_isolate_jobs(Manager *m) {
- Iterator i;
- Unit *u;
- char *k;
- int r;
-
- assert(m);
-
- HASHMAP_FOREACH_KEY(u, k, m->units, i) {
-
- /* ignore aliases */
- if (u->id != k)
- continue;
-
- if (u->ignore_on_isolate)
- continue;
-
- /* No need to stop inactive jobs */
- if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
- continue;
-
- /* Is there already something listed for this? */
- if (hashmap_get(m->transaction_jobs, u))
- continue;
-
- if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, false, false, NULL, NULL)) < 0)
- log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
- }
-
- return 0;
-}
-
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, DBusError *e, Job **_ret) {
- int r;
- Job *ret;
-
- 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));
-
- if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false,
- mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
- mode == JOB_IGNORE_DEPENDENCIES, e, &ret)) < 0) {
- transaction_abort(m);
- return r;
- }
-
- if (mode == JOB_ISOLATE)
- if ((r = transaction_add_isolate_jobs(m)) < 0) {
- transaction_abort(m);
- return r;
- }
-
- if ((r = transaction_activate(m, mode, e)) < 0)
- return r;
-
- log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) ret->id);
-
- if (_ret)
- *_ret = ret;
-
- return 0;
-}
-
-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 = file_name_from_path(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);
-
- transaction_abort(m);
-
- while ((j = hashmap_first(m->jobs)))
- job_finish_and_invalidate(j, JOB_CANCELED);
-}
-
-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;
- int wait_msec = -1;
-
- 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;
-
- /* 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;
- }
-
- while (m->exit_code == MANAGER_RUNNING) {
- struct epoll_event event;
- int n;
-
- if (wait_msec >= 0)
- 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;
-
- 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_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u) {
- char *n;
- Unit *u;
-
- assert(m);
- assert(s);
- assert(_u);
-
- if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
- return -EINVAL;
-
- if (!(n = bus_path_unescape(s+31)))
- return -ENOMEM;
-
- u = manager_get_unit(m, n);
- free(n);
-
- if (!u)
- return -ENOENT;
-
- *_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) {
- log_warning("Failed to send audit message: %m");
-
- if (errno == EPERM) {
- /* We aren't allowed to send audit messages?
- * Then let's not retry again, to avoid
- * spamming the user with the same and same
- * messages over and over. */
-
- audit_close(m->audit_fd);
- m->audit_fd = -1;
- }
- }
-
- 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 = 0, initrd_usec = 0, userspace_usec = 0, total_usec = 0;
-
- assert(m);
-
- if (dual_timestamp_is_set(&m->finish_timestamp))
- return;
-
- if (hashmap_size(m->jobs) > 0)
- 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);
-
- if (!(l = strv_copy(controllers)))
- return -ENOMEM;
-
- strv_free(m->default_controllers);
- m->default_controllers = l;
-
- 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);
diff --git a/src/manager.h b/src/manager.h
deleted file mode 100644
index 9ecc926..0000000
--- a/src/manager.h
+++ /dev/null
@@ -1,304 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foomanagerhfoo
-#define foomanagerhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <dbus/dbus.h>
-
-#include "fdset.h"
-
-/* Enforce upper limit how many names we allow */
-#define MANAGER_MAX_NAMES 131072 /* 128K */
-
-typedef struct Manager Manager;
-typedef enum WatchType WatchType;
-typedef struct Watch Watch;
-
-typedef enum ManagerExitCode {
- MANAGER_RUNNING,
- MANAGER_EXIT,
- MANAGER_RELOAD,
- MANAGER_REEXECUTE,
- MANAGER_REBOOT,
- MANAGER_POWEROFF,
- MANAGER_HALT,
- MANAGER_KEXEC,
- _MANAGER_EXIT_CODE_MAX,
- _MANAGER_EXIT_CODE_INVALID = -1
-} ManagerExitCode;
-
-typedef enum ManagerRunningAs {
- MANAGER_SYSTEM,
- MANAGER_USER,
- _MANAGER_RUNNING_AS_MAX,
- _MANAGER_RUNNING_AS_INVALID = -1
-} ManagerRunningAs;
-
-enum WatchType {
- WATCH_INVALID,
- WATCH_SIGNAL,
- WATCH_NOTIFY,
- WATCH_FD,
- WATCH_UNIT_TIMER,
- WATCH_JOB_TIMER,
- WATCH_MOUNT,
- WATCH_SWAP,
- WATCH_UDEV,
- WATCH_DBUS_WATCH,
- WATCH_DBUS_TIMEOUT
-};
-
-struct Watch {
- int fd;
- WatchType type;
- union {
- struct Unit *unit;
- struct Job *job;
- DBusWatch *bus_watch;
- DBusTimeout *bus_timeout;
- } data;
- bool fd_is_dupped:1;
- bool socket_accept:1;
-};
-
-#include "unit.h"
-#include "job.h"
-#include "hashmap.h"
-#include "list.h"
-#include "set.h"
-#include "dbus.h"
-#include "path-lookup.h"
-
-struct Manager {
- /* Note that the set of units we know of is allowed to be
- * inconsistent. However the subset of it that is loaded may
- * not, and the list of jobs may neither. */
-
- /* Active jobs and units */
- Hashmap *units; /* name string => Unit object n:1 */
- Hashmap *jobs; /* job id => Job object 1:1 */
-
- /* To make it easy to iterate through the units of a specific
- * type we maintain a per type linked list */
- LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]);
-
- /* Units that need to be loaded */
- LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */
-
- /* Jobs that need to be run */
- LIST_HEAD(Job, run_queue); /* more a stack than a queue, too */
-
- /* Units and jobs that have not yet been announced via
- * D-Bus. When something about a job changes it is added here
- * if it is not in there yet. This allows easy coalescing of
- * D-Bus change signals. */
- LIST_HEAD(Unit, dbus_unit_queue);
- LIST_HEAD(Job, dbus_job_queue);
-
- /* Units to remove */
- LIST_HEAD(Unit, cleanup_queue);
-
- /* Units to check when doing GC */
- LIST_HEAD(Unit, gc_queue);
-
- /* Jobs to be added */
- Hashmap *transaction_jobs; /* Unit object => Job object list 1:1 */
- JobDependency *transaction_anchor;
-
- Hashmap *watch_pids; /* pid => Unit object n:1 */
-
- char *notify_socket;
-
- Watch notify_watch;
- Watch signal_watch;
-
- int epoll_fd;
-
- unsigned n_snapshots;
-
- LookupPaths lookup_paths;
- Set *unit_path_cache;
-
- char **environment;
- char **default_controllers;
-
- usec_t runtime_watchdog;
- usec_t shutdown_watchdog;
-
- dual_timestamp initrd_timestamp;
- dual_timestamp startup_timestamp;
- dual_timestamp finish_timestamp;
-
- char *generator_unit_path;
-
- /* Data specific to the device subsystem */
- struct udev* udev;
- struct udev_monitor* udev_monitor;
- Watch udev_watch;
- Hashmap *devices_by_sysfs;
-
- /* Data specific to the mount subsystem */
- FILE *proc_self_mountinfo;
- Watch mount_watch;
-
- /* Data specific to the swap filesystem */
- FILE *proc_swaps;
- Hashmap *swaps_by_proc_swaps;
- bool request_reload;
- Watch swap_watch;
-
- /* Data specific to the D-Bus subsystem */
- DBusConnection *api_bus, *system_bus;
- DBusServer *private_bus;
- Set *bus_connections, *bus_connections_for_dispatch;
-
- DBusMessage *queued_message; /* This is used during reloading:
- * before the reload we queue the
- * reply message here, and
- * afterwards we send it */
- DBusConnection *queued_message_connection; /* The connection to send the queued message on */
-
- Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */
- int32_t name_data_slot;
- int32_t conn_data_slot;
- int32_t subscribed_data_slot;
-
- uint32_t current_job_id;
- uint32_t default_unit_job_id;
-
- /* Data specific to the Automount subsystem */
- int dev_autofs_fd;
-
- /* Data specific to the cgroup subsystem */
- Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
- char *cgroup_hierarchy;
-
- usec_t gc_queue_timestamp;
- int gc_marker;
- unsigned n_in_gc_queue;
-
- /* Make sure the user cannot accidentally unmount our cgroup
- * file system */
- int pin_cgroupfs_fd;
-
- /* Audit fd */
-#ifdef HAVE_AUDIT
- int audit_fd;
-#endif
-
- /* Flags */
- ManagerRunningAs running_as;
- ManagerExitCode exit_code:5;
-
- bool dispatching_load_queue:1;
- bool dispatching_run_queue:1;
- bool dispatching_dbus_queue:1;
-
- bool taint_usr:1;
-
- bool show_status;
- bool confirm_spawn;
-#ifdef HAVE_SYSV_COMPAT
- bool sysv_console;
-#endif
- bool mount_auto;
- bool swap_auto;
-
- ExecOutput default_std_output, default_std_error;
-
- /* non-zero if we are reloading or reexecuting, */
- int n_reloading;
-
- unsigned n_installed_jobs;
- unsigned n_failed_jobs;
-};
-
-int manager_new(ManagerRunningAs running_as, Manager **m);
-void manager_free(Manager *m);
-
-int manager_enumerate(Manager *m);
-int manager_coldplug(Manager *m);
-int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
-
-Job *manager_get_job(Manager *m, uint32_t id);
-Unit *manager_get_unit(Manager *m, const char *name);
-
-int manager_get_unit_from_dbus_path(Manager *m, const char *s, Unit **_u);
-int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
-
-int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret);
-int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret);
-
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, DBusError *e, Job **_ret);
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, DBusError *e, Job **_ret);
-
-void manager_dump_units(Manager *s, FILE *f, const char *prefix);
-void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
-
-void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies);
-
-void manager_clear_jobs(Manager *m);
-
-unsigned manager_dispatch_load_queue(Manager *m);
-unsigned manager_dispatch_run_queue(Manager *m);
-unsigned manager_dispatch_dbus_queue(Manager *m);
-
-int manager_set_default_controllers(Manager *m, char **controllers);
-
-int manager_loop(Manager *m);
-
-void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
-void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid);
-
-int manager_open_serialization(Manager *m, FILE **_f);
-
-int manager_serialize(Manager *m, FILE *f, FDSet *fds);
-int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
-
-int manager_reload(Manager *m);
-
-bool manager_is_booting_or_shutting_down(Manager *m);
-
-void manager_reset_failed(Manager *m);
-
-void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
-void manager_send_unit_plymouth(Manager *m, Unit *u);
-
-bool manager_unit_pending_inactive(Manager *m, const char *name);
-
-void manager_check_finished(Manager *m);
-
-void manager_run_generators(Manager *m);
-void manager_undo_generators(Manager *m);
-
-void manager_recheck_journal(Manager *m);
-
-void manager_set_show_status(Manager *m, bool b);
-bool manager_get_show_status(Manager *m);
-
-const char *manager_running_as_to_string(ManagerRunningAs i);
-ManagerRunningAs manager_running_as_from_string(const char *s);
-
-#endif
diff --git a/src/mount.c b/src/mount.c
deleted file mode 100644
index 7dbeaf9..0000000
--- a/src/mount.c
+++ /dev/null
@@ -1,1930 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdio.h>
-#include <mntent.h>
-#include <sys/epoll.h>
-#include <signal.h>
-
-#include "unit.h"
-#include "mount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "mount-setup.h"
-#include "unit-name.h"
-#include "dbus-mount.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "exit-status.h"
-#include "def.h"
-
-static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = UNIT_INACTIVE,
- [MOUNT_MOUNTING] = UNIT_ACTIVATING,
- [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE,
- [MOUNT_MOUNTED] = UNIT_ACTIVE,
- [MOUNT_REMOUNTING] = UNIT_RELOADING,
- [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING,
- [MOUNT_MOUNTING_SIGTERM] = UNIT_DEACTIVATING,
- [MOUNT_MOUNTING_SIGKILL] = UNIT_DEACTIVATING,
- [MOUNT_REMOUNTING_SIGTERM] = UNIT_RELOADING,
- [MOUNT_REMOUNTING_SIGKILL] = UNIT_RELOADING,
- [MOUNT_UNMOUNTING_SIGTERM] = UNIT_DEACTIVATING,
- [MOUNT_UNMOUNTING_SIGKILL] = UNIT_DEACTIVATING,
- [MOUNT_FAILED] = UNIT_FAILED
-};
-
-static void mount_init(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- m->timeout_usec = DEFAULT_TIMEOUT_USEC;
- m->directory_mode = 0755;
-
- exec_context_init(&m->exec_context);
-
- /* The stdio/kmsg bridge socket is on /, in order to avoid a
- * dep loop, don't use kmsg logging for -.mount */
- if (!unit_has_name(u, "-.mount")) {
- m->exec_context.std_output = u->manager->default_std_output;
- m->exec_context.std_error = u->manager->default_std_error;
- }
-
- /* We need to make sure that /bin/mount is always called in
- * the same process group as us, so that the autofs kernel
- * side doesn't send us another mount request while we are
- * already trying to comply its last one. */
- m->exec_context.same_pgrp = true;
-
- m->timer_watch.type = WATCH_INVALID;
-
- m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
-
- UNIT(m)->ignore_on_isolate = true;
-}
-
-static void mount_unwatch_control_pid(Mount *m) {
- assert(m);
-
- if (m->control_pid <= 0)
- return;
-
- unit_unwatch_pid(UNIT(m), m->control_pid);
- m->control_pid = 0;
-}
-
-static void mount_parameters_done(MountParameters *p) {
- assert(p);
-
- free(p->what);
- free(p->options);
- free(p->fstype);
-
- p->what = p->options = p->fstype = NULL;
-}
-
-static void mount_done(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- free(m->where);
- m->where = NULL;
-
- mount_parameters_done(&m->parameters_etc_fstab);
- mount_parameters_done(&m->parameters_proc_self_mountinfo);
- mount_parameters_done(&m->parameters_fragment);
-
- exec_context_done(&m->exec_context);
- exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
- m->control_command = NULL;
-
- mount_unwatch_control_pid(m);
-
- unit_unwatch_timer(u, &m->timer_watch);
-}
-
-static MountParameters* get_mount_parameters_configured(Mount *m) {
- assert(m);
-
- if (m->from_fragment)
- return &m->parameters_fragment;
- else if (m->from_etc_fstab)
- return &m->parameters_etc_fstab;
-
- return NULL;
-}
-
-static MountParameters* get_mount_parameters(Mount *m) {
- assert(m);
-
- if (m->from_proc_self_mountinfo)
- return &m->parameters_proc_self_mountinfo;
-
- return get_mount_parameters_configured(m);
-}
-
-static int mount_add_mount_links(Mount *m) {
- Unit *other;
- int r;
- MountParameters *pm;
-
- assert(m);
-
- pm = get_mount_parameters_configured(m);
-
- /* Adds in links to other mount points that might lie below or
- * above us in the hierarchy */
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) {
- Mount *n = MOUNT(other);
- MountParameters *pn;
-
- if (n == m)
- continue;
-
- if (UNIT(n)->load_state != UNIT_LOADED)
- continue;
-
- pn = get_mount_parameters_configured(n);
-
- if (path_startswith(m->where, n->where)) {
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
- return r;
-
- if (pn)
- if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
- return r;
-
- } else if (path_startswith(n->where, m->where)) {
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
- return r;
-
- if (pm)
- if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
-
- } else if (pm && pm->what && path_startswith(pm->what, n->where)) {
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
- return r;
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
- return r;
-
- } else if (pn && pn->what && path_startswith(pn->what, m->where)) {
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
- return r;
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
- }
- }
-
- return 0;
-}
-
-static int mount_add_swap_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP])
- if ((r = swap_add_one_mount_link(SWAP(other), m)) < 0)
- return r;
-
- return 0;
-}
-
-static int mount_add_path_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH])
- if ((r = path_add_one_mount_link(PATH(other), m)) < 0)
- return r;
-
- return 0;
-}
-
-static int mount_add_automount_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT])
- if ((r = automount_add_one_mount_link(AUTOMOUNT(other), m)) < 0)
- return r;
-
- return 0;
-}
-
-static int mount_add_socket_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET])
- if ((r = socket_add_one_mount_link(SOCKET(other), m)) < 0)
- return r;
-
- return 0;
-}
-
-static char* mount_test_option(const char *haystack, const char *needle) {
- struct mntent me;
-
- assert(needle);
-
- /* Like glibc's hasmntopt(), but works on a string, not a
- * struct mntent */
-
- if (!haystack)
- return NULL;
-
- zero(me);
- me.mnt_opts = (char*) haystack;
-
- return hasmntopt(&me, needle);
-}
-
-static bool mount_is_network(MountParameters *p) {
- assert(p);
-
- if (mount_test_option(p->options, "_netdev"))
- return true;
-
- if (p->fstype && fstype_is_network(p->fstype))
- return true;
-
- return false;
-}
-
-static bool mount_is_bind(MountParameters *p) {
- assert(p);
-
- if (mount_test_option(p->options, "bind"))
- return true;
-
- if (p->fstype && streq(p->fstype, "bind"))
- return true;
-
- return false;
-}
-
-static bool needs_quota(MountParameters *p) {
- assert(p);
-
- if (mount_is_network(p))
- return false;
-
- if (mount_is_bind(p))
- return false;
-
- return mount_test_option(p->options, "usrquota") ||
- mount_test_option(p->options, "grpquota") ||
- mount_test_option(p->options, "quota") ||
- mount_test_option(p->options, "usrjquota") ||
- mount_test_option(p->options, "grpjquota");
-}
-
-static int mount_add_fstab_links(Mount *m) {
- const char *target, *after, *tu_wants = NULL;
- MountParameters *p;
- Unit *tu;
- int r;
- bool noauto, nofail, handle, automount;
-
- assert(m);
-
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
- return 0;
-
- if (!(p = get_mount_parameters_configured(m)))
- return 0;
-
- if (p != &m->parameters_etc_fstab)
- return 0;
-
- noauto = !!mount_test_option(p->options, "noauto");
- nofail = !!mount_test_option(p->options, "nofail");
- automount =
- mount_test_option(p->options, "comment=systemd.automount") ||
- mount_test_option(p->options, "x-systemd-automount");
- handle =
- automount ||
- mount_test_option(p->options, "comment=systemd.mount") ||
- mount_test_option(p->options, "x-systemd-mount") ||
- UNIT(m)->manager->mount_auto;
-
- if (mount_is_network(p)) {
- target = SPECIAL_REMOTE_FS_TARGET;
- after = tu_wants = SPECIAL_REMOTE_FS_PRE_TARGET;
- } else {
- target = SPECIAL_LOCAL_FS_TARGET;
- after = SPECIAL_LOCAL_FS_PRE_TARGET;
- }
-
- r = manager_load_unit(UNIT(m)->manager, target, NULL, NULL, &tu);
- if (r < 0)
- return r;
-
- if (tu_wants) {
- r = unit_add_dependency_by_name(tu, UNIT_WANTS, tu_wants, NULL, true);
- if (r < 0)
- return r;
- }
-
- if (after) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
- if (r < 0)
- return r;
- }
-
- if (automount) {
- Unit *am;
-
- if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0)
- return r;
-
- /* If auto is configured as well also pull in the
- * mount right-away, but don't rely on it. */
- if (!noauto) /* automount + auto */
- if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true)) < 0)
- return r;
-
- /* Install automount unit */
- if (!nofail) /* automount + fail */
- return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, am, true);
- else /* automount + nofail */
- return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_WANTS, am, true);
-
- } else if (handle && !noauto) {
-
- /* Automatically add mount points that aren't natively
- * configured to local-fs.target */
-
- if (!nofail) /* auto + fail */
- return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
- else /* auto + nofail */
- return unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true);
- }
-
- return 0;
-}
-
-static int mount_add_device_links(Mount *m) {
- MountParameters *p;
- int r;
-
- assert(m);
-
- if (!(p = get_mount_parameters_configured(m)))
- return 0;
-
- if (!p->what)
- return 0;
-
- if (!mount_is_bind(p) &&
- !path_equal(m->where, "/") &&
- p == &m->parameters_etc_fstab) {
- bool nofail, noauto;
-
- noauto = !!mount_test_option(p->options, "noauto");
- nofail = !!mount_test_option(p->options, "nofail");
-
- if ((r = unit_add_node_link(UNIT(m), p->what,
- !noauto && nofail &&
- UNIT(m)->manager->running_as == MANAGER_SYSTEM)) < 0)
- return r;
- }
-
- if (p->passno > 0 &&
- !mount_is_bind(p) &&
- UNIT(m)->manager->running_as == MANAGER_SYSTEM &&
- !path_equal(m->where, "/")) {
- char *name;
- Unit *fsck;
- /* Let's add in the fsck service */
-
- /* aka SPECIAL_FSCK_SERVICE */
- if (!(name = unit_name_from_path_instance("fsck", p->what, ".service")))
- return -ENOMEM;
-
- if ((r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck)) < 0) {
- log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
- free(name);
- return r;
- }
-
- free(name);
-
- SERVICE(fsck)->fsck_passno = p->passno;
-
- if ((r = unit_add_two_dependencies(UNIT(m), UNIT_AFTER, UNIT_REQUIRES, fsck, true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int mount_add_default_dependencies(Mount *m) {
- int r;
- MountParameters *p;
-
- assert(m);
-
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
- return 0;
-
- p = get_mount_parameters_configured(m);
- if (p && needs_quota(p)) {
- if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true)) < 0 ||
- (r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true)) < 0)
- return r;
- }
-
- if (!path_equal(m->where, "/"))
- if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
- return r;
-
- return 0;
-}
-
-static int mount_fix_timeouts(Mount *m) {
- MountParameters *p;
- const char *timeout = NULL;
- Unit *other;
- Iterator i;
- usec_t u;
- char *t;
- int r;
-
- assert(m);
-
- if (!(p = get_mount_parameters_configured(m)))
- return 0;
-
- /* Allow configuration how long we wait for a device that
- * backs a mount point to show up. This is useful to support
- * endless device timeouts for devices that show up only after
- * user input, like crypto devices. */
-
- if ((timeout = mount_test_option(p->options, "comment=systemd.device-timeout")))
- timeout += 31;
- else if ((timeout = mount_test_option(p->options, "x-systemd-device-timeout")))
- timeout += 25;
- else
- return 0;
-
- t = strndup(timeout, strcspn(timeout, ",;" WHITESPACE));
- if (!t)
- return -ENOMEM;
-
- r = parse_usec(t, &u);
- free(t);
-
- if (r < 0) {
- log_warning("Failed to parse timeout for %s, ignoring: %s", m->where, timeout);
- return r;
- }
-
- SET_FOREACH(other, UNIT(m)->dependencies[UNIT_AFTER], i) {
- if (other->type != UNIT_DEVICE)
- continue;
-
- other->job_timeout = u;
- }
-
- return 0;
-}
-
-static int mount_verify(Mount *m) {
- bool b;
- char *e;
- assert(m);
-
- if (UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (!m->from_etc_fstab && !m->from_fragment && !m->from_proc_self_mountinfo)
- return -ENOENT;
-
- if (!(e = unit_name_from_path(m->where, ".mount")))
- return -ENOMEM;
-
- b = unit_has_name(UNIT(m), e);
- free(e);
-
- if (!b) {
- log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(m)->id);
- return -EINVAL;
- }
-
- if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) {
- log_error("Cannot create mount unit for API file system %s. Refusing.", m->where);
- return -EINVAL;
- }
-
- if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
- log_error("%s's What setting is missing. Refusing.", UNIT(m)->id);
- return -EBADMSG;
- }
-
- if (m->exec_context.pam_name && m->exec_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(m)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int mount_load(Unit *u) {
- Mount *m = MOUNT(u);
- int r;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
- return r;
-
- /* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
- if ((r = unit_add_exec_dependencies(u, &m->exec_context)) < 0)
- return r;
-
- if (UNIT(m)->fragment_path)
- m->from_fragment = true;
- else if (m->from_etc_fstab)
- /* We always add several default dependencies to fstab mounts,
- * but we do not want the implicit complementing of Wants= with After=
- * in the target unit that this mount unit will be hooked into. */
- UNIT(m)->default_dependencies = false;
-
- if (!m->where)
- if (!(m->where = unit_name_to_path(u->id)))
- return -ENOMEM;
-
- path_kill_slashes(m->where);
-
- if (!UNIT(m)->description)
- if ((r = unit_set_description(u, m->where)) < 0)
- return r;
-
- if ((r = mount_add_device_links(m)) < 0)
- return r;
-
- if ((r = mount_add_mount_links(m)) < 0)
- return r;
-
- if ((r = mount_add_socket_links(m)) < 0)
- return r;
-
- if ((r = mount_add_swap_links(m)) < 0)
- return r;
-
- if ((r = mount_add_path_links(m)) < 0)
- return r;
-
- if ((r = mount_add_automount_links(m)) < 0)
- return r;
-
- if ((r = mount_add_fstab_links(m)) < 0)
- return r;
-
- if (UNIT(m)->default_dependencies || m->from_etc_fstab)
- if ((r = mount_add_default_dependencies(m)) < 0)
- return r;
-
- if ((r = unit_add_default_cgroups(u)) < 0)
- return r;
-
- mount_fix_timeouts(m);
- }
-
- return mount_verify(m);
-}
-
-static int mount_notify_automount(Mount *m, int status) {
- Unit *p;
- int r;
- Iterator i;
-
- assert(m);
-
- SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
- if (p->type == UNIT_AUTOMOUNT) {
- r = automount_send_ready(AUTOMOUNT(p), status);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static void mount_set_state(Mount *m, MountState state) {
- MountState old_state;
- assert(m);
-
- old_state = m->state;
- m->state = state;
-
- if (state != MOUNT_MOUNTING &&
- state != MOUNT_MOUNTING_DONE &&
- state != MOUNT_REMOUNTING &&
- state != MOUNT_UNMOUNTING &&
- state != MOUNT_MOUNTING_SIGTERM &&
- state != MOUNT_MOUNTING_SIGKILL &&
- state != MOUNT_UNMOUNTING_SIGTERM &&
- state != MOUNT_UNMOUNTING_SIGKILL &&
- state != MOUNT_REMOUNTING_SIGTERM &&
- state != MOUNT_REMOUNTING_SIGKILL) {
- unit_unwatch_timer(UNIT(m), &m->timer_watch);
- mount_unwatch_control_pid(m);
- m->control_command = NULL;
- m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
- }
-
- if (state == MOUNT_MOUNTED ||
- state == MOUNT_REMOUNTING)
- mount_notify_automount(m, 0);
- else if (state == MOUNT_DEAD ||
- state == MOUNT_UNMOUNTING ||
- state == MOUNT_MOUNTING_SIGTERM ||
- state == MOUNT_MOUNTING_SIGKILL ||
- state == MOUNT_REMOUNTING_SIGTERM ||
- state == MOUNT_REMOUNTING_SIGKILL ||
- state == MOUNT_UNMOUNTING_SIGTERM ||
- state == MOUNT_UNMOUNTING_SIGKILL ||
- state == MOUNT_FAILED)
- mount_notify_automount(m, -ENODEV);
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(m)->id,
- mount_state_to_string(old_state),
- mount_state_to_string(state));
-
- unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
- m->reload_result = MOUNT_SUCCESS;
-}
-
-static int mount_coldplug(Unit *u) {
- Mount *m = MOUNT(u);
- MountState new_state = MOUNT_DEAD;
- int r;
-
- assert(m);
- assert(m->state == MOUNT_DEAD);
-
- if (m->deserialized_state != m->state)
- new_state = m->deserialized_state;
- else if (m->from_proc_self_mountinfo)
- new_state = MOUNT_MOUNTED;
-
- if (new_state != m->state) {
-
- if (new_state == MOUNT_MOUNTING ||
- new_state == MOUNT_MOUNTING_DONE ||
- new_state == MOUNT_REMOUNTING ||
- new_state == MOUNT_UNMOUNTING ||
- new_state == MOUNT_MOUNTING_SIGTERM ||
- new_state == MOUNT_MOUNTING_SIGKILL ||
- new_state == MOUNT_UNMOUNTING_SIGTERM ||
- new_state == MOUNT_UNMOUNTING_SIGKILL ||
- new_state == MOUNT_REMOUNTING_SIGTERM ||
- new_state == MOUNT_REMOUNTING_SIGKILL) {
-
- if (m->control_pid <= 0)
- return -EBADMSG;
-
- if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
- return r;
-
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
- return r;
- }
-
- mount_set_state(m, new_state);
- }
-
- return 0;
-}
-
-static void mount_dump(Unit *u, FILE *f, const char *prefix) {
- Mount *m = MOUNT(u);
- MountParameters *p;
-
- assert(m);
- assert(f);
-
- p = get_mount_parameters(m);
-
- fprintf(f,
- "%sMount State: %s\n"
- "%sResult: %s\n"
- "%sWhere: %s\n"
- "%sWhat: %s\n"
- "%sFile System Type: %s\n"
- "%sOptions: %s\n"
- "%sFrom /etc/fstab: %s\n"
- "%sFrom /proc/self/mountinfo: %s\n"
- "%sFrom fragment: %s\n"
- "%sDirectoryMode: %04o\n",
- prefix, mount_state_to_string(m->state),
- prefix, mount_result_to_string(m->result),
- prefix, m->where,
- prefix, strna(p->what),
- prefix, strna(p->fstype),
- prefix, strna(p->options),
- prefix, yes_no(m->from_etc_fstab),
- prefix, yes_no(m->from_proc_self_mountinfo),
- prefix, yes_no(m->from_fragment),
- prefix, m->directory_mode);
-
- if (m->control_pid > 0)
- fprintf(f,
- "%sControl PID: %lu\n",
- prefix, (unsigned long) m->control_pid);
-
- exec_context_dump(&m->exec_context, f, prefix);
-}
-
-static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
- pid_t pid;
- int r;
-
- assert(m);
- assert(c);
- assert(_pid);
-
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
- goto fail;
-
- if ((r = exec_spawn(c,
- NULL,
- &m->exec_context,
- NULL, 0,
- UNIT(m)->manager->environment,
- true,
- true,
- true,
- UNIT(m)->manager->confirm_spawn,
- UNIT(m)->cgroup_bondings,
- UNIT(m)->cgroup_attributes,
- &pid)) < 0)
- goto fail;
-
- if ((r = unit_watch_pid(UNIT(m), pid)) < 0)
- /* FIXME: we need to do something here */
- goto fail;
-
- *_pid = pid;
-
- return 0;
-
-fail:
- unit_unwatch_timer(UNIT(m), &m->timer_watch);
-
- return r;
-}
-
-static void mount_enter_dead(Mount *m, MountResult f) {
- assert(m);
-
- if (f != MOUNT_SUCCESS)
- m->result = f;
-
- mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
-}
-
-static void mount_enter_mounted(Mount *m, MountResult f) {
- assert(m);
-
- if (f != MOUNT_SUCCESS)
- m->result = f;
-
- mount_set_state(m, MOUNT_MOUNTED);
-}
-
-static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
- int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
-
- assert(m);
-
- if (f != MOUNT_SUCCESS)
- m->result = f;
-
- if (m->exec_context.kill_mode != KILL_NONE) {
- int sig = (state == MOUNT_MOUNTING_SIGTERM ||
- state == MOUNT_UNMOUNTING_SIGTERM ||
- state == MOUNT_REMOUNTING_SIGTERM) ? m->exec_context.kill_signal : SIGKILL;
-
- if (m->control_pid > 0) {
- if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning("Failed to kill control process %li: %m", (long) m->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (m->exec_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0)
- goto fail;
-
- if ((r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, pid_set)) < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning("Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
-
- if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
- goto fail;
-
- mount_set_state(m, state);
- } else if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
- mount_enter_mounted(m, MOUNT_SUCCESS);
- else
- mount_enter_dead(m, MOUNT_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to kill processes: %s", UNIT(m)->id, strerror(-r));
-
- if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
- mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
- else
- mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
-}
-
-static void mount_enter_unmounting(Mount *m) {
- int r;
-
- assert(m);
-
- m->control_command_id = MOUNT_EXEC_UNMOUNT;
- m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
-
- if ((r = exec_command_set(
- m->control_command,
- "/bin/umount",
- m->where,
- NULL)) < 0)
- goto fail;
-
- mount_unwatch_control_pid(m);
-
- if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
- goto fail;
-
- mount_set_state(m, MOUNT_UNMOUNTING);
-
- return;
-
-fail:
- log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r));
- mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
-}
-
-static void mount_enter_mounting(Mount *m) {
- int r;
- MountParameters *p;
-
- assert(m);
-
- m->control_command_id = MOUNT_EXEC_MOUNT;
- m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
-
- mkdir_p(m->where, m->directory_mode);
-
- /* Create the source directory for bind-mounts if needed */
- p = get_mount_parameters_configured(m);
- if (p && mount_is_bind(p))
- mkdir_p(p->what, m->directory_mode);
-
- if (m->from_fragment)
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->parameters_fragment.what,
- m->where,
- "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
- m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options,
- NULL);
- else if (m->from_etc_fstab)
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->where,
- NULL);
- else
- r = -ENOENT;
-
- if (r < 0)
- goto fail;
-
- mount_unwatch_control_pid(m);
-
- if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
- goto fail;
-
- mount_set_state(m, MOUNT_MOUNTING);
-
- return;
-
-fail:
- log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r));
- mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
-}
-
-static void mount_enter_mounting_done(Mount *m) {
- assert(m);
-
- mount_set_state(m, MOUNT_MOUNTING_DONE);
-}
-
-static void mount_enter_remounting(Mount *m) {
- int r;
-
- assert(m);
-
- m->control_command_id = MOUNT_EXEC_REMOUNT;
- m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
-
- if (m->from_fragment) {
- char *buf = NULL;
- const char *o;
-
- if (m->parameters_fragment.options) {
- if (!(buf = strappend("remount,", m->parameters_fragment.options))) {
- r = -ENOMEM;
- goto fail;
- }
-
- o = buf;
- } else
- o = "remount";
-
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->parameters_fragment.what,
- m->where,
- "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
- "-o", o,
- NULL);
-
- free(buf);
- } else if (m->from_etc_fstab)
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->where,
- "-o", "remount",
- NULL);
- else
- r = -ENOENT;
-
- if (r < 0)
- goto fail;
-
- mount_unwatch_control_pid(m);
-
- if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
- goto fail;
-
- mount_set_state(m, MOUNT_REMOUNTING);
-
- return;
-
-fail:
- log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r));
- m->reload_result = MOUNT_FAILURE_RESOURCES;
- mount_enter_mounted(m, MOUNT_SUCCESS);
-}
-
-static int mount_start(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- /* We cannot fulfill this request right now, try again later
- * please! */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
- return -EAGAIN;
-
- /* Already on it! */
- if (m->state == MOUNT_MOUNTING)
- return 0;
-
- assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
-
- m->result = MOUNT_SUCCESS;
- m->reload_result = MOUNT_SUCCESS;
-
- mount_enter_mounting(m);
- return 0;
-}
-
-static int mount_stop(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- /* Already on it */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
- return 0;
-
- assert(m->state == MOUNT_MOUNTING ||
- m->state == MOUNT_MOUNTING_DONE ||
- m->state == MOUNT_MOUNTED ||
- m->state == MOUNT_REMOUNTING ||
- m->state == MOUNT_REMOUNTING_SIGTERM ||
- m->state == MOUNT_REMOUNTING_SIGKILL);
-
- mount_enter_unmounting(m);
- return 0;
-}
-
-static int mount_reload(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- if (m->state == MOUNT_MOUNTING_DONE)
- return -EAGAIN;
-
- assert(m->state == MOUNT_MOUNTED);
-
- mount_enter_remounting(m);
- return 0;
-}
-
-static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
- Mount *m = MOUNT(u);
-
- assert(m);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", mount_state_to_string(m->state));
- unit_serialize_item(u, f, "result", mount_result_to_string(m->result));
- unit_serialize_item(u, f, "reload-result", mount_result_to_string(m->reload_result));
-
- if (m->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) m->control_pid);
-
- if (m->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
-
- return 0;
-}
-
-static int mount_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Mount *m = MOUNT(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- MountState state;
-
- if ((state = mount_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- m->deserialized_state = state;
- } else if (streq(key, "result")) {
- MountResult f;
-
- f = mount_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != MOUNT_SUCCESS)
- m->result = f;
-
- } else if (streq(key, "reload-result")) {
- MountResult f;
-
- f = mount_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse reload result value %s", value);
- else if (f != MOUNT_SUCCESS)
- m->reload_result = f;
-
- } else if (streq(key, "control-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
- else
- m->control_pid = pid;
- } else if (streq(key, "control-command")) {
- MountExecCommand id;
-
- if ((id = mount_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
- else {
- m->control_command_id = id;
- m->control_command = m->exec_command + id;
- }
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState mount_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[MOUNT(u)->state];
-}
-
-static const char *mount_sub_state_to_string(Unit *u) {
- assert(u);
-
- return mount_state_to_string(MOUNT(u)->state);
-}
-
-static bool mount_check_gc(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- return m->from_etc_fstab || m->from_proc_self_mountinfo;
-}
-
-static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- Mount *m = MOUNT(u);
- MountResult f;
-
- assert(m);
- assert(pid >= 0);
-
- if (pid != m->control_pid)
- return;
-
- m->control_pid = 0;
-
- if (is_clean_exit(code, status))
- f = MOUNT_SUCCESS;
- else if (code == CLD_EXITED)
- f = MOUNT_FAILURE_EXIT_CODE;
- else if (code == CLD_KILLED)
- f = MOUNT_FAILURE_SIGNAL;
- else if (code == CLD_DUMPED)
- f = MOUNT_FAILURE_CORE_DUMP;
- else
- assert_not_reached("Unknown code");
-
- if (f != MOUNT_SUCCESS)
- m->result = f;
-
- if (m->control_command) {
- exec_status_exit(&m->control_command->exec_status, &m->exec_context, pid, code, status);
-
- m->control_command = NULL;
- m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
- }
-
- log_full(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
-
- /* Note that mount(8) returning and the kernel sending us a
- * mount table change event might happen out-of-order. If an
- * operation succeed we assume the kernel will follow soon too
- * and already change into the resulting state. If it fails
- * we check if the kernel still knows about the mount. and
- * change state accordingly. */
-
- switch (m->state) {
-
- case MOUNT_MOUNTING:
- case MOUNT_MOUNTING_DONE:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_MOUNTING_SIGTERM:
-
- if (f == MOUNT_SUCCESS)
- mount_enter_mounted(m, f);
- else if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, f);
- else
- mount_enter_dead(m, f);
- break;
-
- case MOUNT_REMOUNTING:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGTERM:
-
- m->reload_result = f;
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_SUCCESS);
- else
- mount_enter_dead(m, MOUNT_SUCCESS);
-
- break;
-
- case MOUNT_UNMOUNTING:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
-
- if (f == MOUNT_SUCCESS)
- mount_enter_dead(m, f);
- else if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, f);
- else
- mount_enter_dead(m, f);
- break;
-
- default:
- assert_not_reached("Uh, control process died at wrong time.");
- }
-
- /* Notify clients about changed exit status */
- unit_add_to_dbus_queue(u);
-}
-
-static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
- Mount *m = MOUNT(u);
-
- assert(m);
- assert(elapsed == 1);
- assert(w == &m->timer_watch);
-
- switch (m->state) {
-
- case MOUNT_MOUNTING:
- case MOUNT_MOUNTING_DONE:
- log_warning("%s mounting timed out. Stopping.", u->id);
- mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
- break;
-
- case MOUNT_REMOUNTING:
- log_warning("%s remounting timed out. Stopping.", u->id);
- m->reload_result = MOUNT_FAILURE_TIMEOUT;
- mount_enter_mounted(m, MOUNT_SUCCESS);
- break;
-
- case MOUNT_UNMOUNTING:
- log_warning("%s unmounting timed out. Stopping.", u->id);
- mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
- break;
-
- case MOUNT_MOUNTING_SIGTERM:
- if (m->exec_context.send_sigkill) {
- log_warning("%s mounting timed out. Killing.", u->id);
- mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
- } else {
- log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id);
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
- }
- break;
-
- case MOUNT_REMOUNTING_SIGTERM:
- if (m->exec_context.send_sigkill) {
- log_warning("%s remounting timed out. Killing.", u->id);
- mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
- } else {
- log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->id);
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
- }
- break;
-
- case MOUNT_UNMOUNTING_SIGTERM:
- if (m->exec_context.send_sigkill) {
- log_warning("%s unmounting timed out. Killing.", u->id);
- mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
- } else {
- log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->id);
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
- }
- break;
-
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGKILL:
- log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id);
-
- if (m->from_proc_self_mountinfo)
- mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
- else
- mount_enter_dead(m, MOUNT_FAILURE_TIMEOUT);
- break;
-
- default:
- assert_not_reached("Timeout at wrong time.");
- }
-}
-
-static int mount_add_one(
- Manager *m,
- const char *what,
- const char *where,
- const char *options,
- const char *fstype,
- int passno,
- bool from_proc_self_mountinfo,
- bool set_flags) {
- int r;
- Unit *u;
- bool delete;
- char *e, *w = NULL, *o = NULL, *f = NULL;
- MountParameters *p;
-
- assert(m);
- assert(what);
- assert(where);
- assert(options);
- assert(fstype);
-
- assert(!set_flags || from_proc_self_mountinfo);
-
- /* Ignore API mount points. They should never be referenced in
- * dependencies ever. */
- if (mount_point_is_api(where) || mount_point_ignore(where))
- return 0;
-
- if (streq(fstype, "autofs"))
- return 0;
-
- /* probably some kind of swap, ignore */
- if (!is_path(where))
- return 0;
-
- e = unit_name_from_path(where, ".mount");
- if (!e)
- return -ENOMEM;
-
- u = manager_get_unit(m, e);
- if (!u) {
- delete = true;
-
- u = unit_new(m, sizeof(Mount));
- if (!u) {
- free(e);
- return -ENOMEM;
- }
-
- r = unit_add_name(u, e);
- free(e);
-
- if (r < 0)
- goto fail;
-
- MOUNT(u)->where = strdup(where);
- if (!MOUNT(u)->where) {
- r = -ENOMEM;
- goto fail;
- }
-
- unit_add_to_load_queue(u);
- } else {
- delete = false;
- free(e);
- }
-
- if (!(w = strdup(what)) ||
- !(o = strdup(options)) ||
- !(f = strdup(fstype))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (from_proc_self_mountinfo) {
- p = &MOUNT(u)->parameters_proc_self_mountinfo;
-
- if (set_flags) {
- MOUNT(u)->is_mounted = true;
- MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo;
- MOUNT(u)->just_changed = !streq_ptr(p->options, o);
- }
-
- MOUNT(u)->from_proc_self_mountinfo = true;
- } else {
- p = &MOUNT(u)->parameters_etc_fstab;
- MOUNT(u)->from_etc_fstab = true;
- }
-
- free(p->what);
- p->what = w;
-
- free(p->options);
- p->options = o;
-
- free(p->fstype);
- p->fstype = f;
-
- p->passno = passno;
-
- unit_add_to_dbus_queue(u);
-
- return 0;
-
-fail:
- free(w);
- free(o);
- free(f);
-
- if (delete && u)
- unit_free(u);
-
- return r;
-}
-
-static int mount_find_pri(char *options) {
- char *end, *pri;
- unsigned long r;
-
- if (!(pri = mount_test_option(options, "pri")))
- return 0;
-
- pri += 4;
-
- errno = 0;
- r = strtoul(pri, &end, 10);
-
- if (errno != 0)
- return -errno;
-
- if (end == pri || (*end != ',' && *end != 0))
- return -EINVAL;
-
- return (int) r;
-}
-
-static int mount_load_etc_fstab(Manager *m) {
- FILE *f;
- int r = 0;
- struct mntent* me;
-
- assert(m);
-
- errno = 0;
- if (!(f = setmntent("/etc/fstab", "r")))
- return -errno;
-
- while ((me = getmntent(f))) {
- char *where, *what;
- int k;
-
- if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(where = strdup(me->mnt_dir))) {
- free(what);
- r = -ENOMEM;
- goto finish;
- }
-
- if (what[0] == '/')
- path_kill_slashes(what);
-
- if (where[0] == '/')
- path_kill_slashes(where);
-
- if (streq(me->mnt_type, "swap")) {
- int pri;
-
- if ((pri = mount_find_pri(me->mnt_opts)) < 0)
- k = pri;
- else
- k = swap_add_one(m,
- what,
- NULL,
- pri,
- !!mount_test_option(me->mnt_opts, "noauto"),
- !!mount_test_option(me->mnt_opts, "nofail"),
- !!mount_test_option(me->mnt_opts, "comment=systemd.swapon"),
- false);
- } else
- k = mount_add_one(m, what, where, me->mnt_opts, me->mnt_type, me->mnt_passno, false, false);
-
- free(what);
- free(where);
-
- if (k < 0)
- r = k;
- }
-
-finish:
-
- endmntent(f);
- return r;
-}
-
-static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
- int r = 0;
- unsigned i;
- char *device, *path, *options, *options2, *fstype, *d, *p, *o;
-
- assert(m);
-
- rewind(m->proc_self_mountinfo);
-
- for (i = 1;; i++) {
- int k;
-
- device = path = options = options2 = fstype = d = p = o = NULL;
-
- if ((k = fscanf(m->proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%ms" /* (6) mount options */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%ms " /* (9) file system type */
- "%ms" /* (10) mount source */
- "%ms" /* (11) mount options 2 */
- "%*[^\n]", /* some rubbish at the end */
- &path,
- &options,
- &fstype,
- &device,
- &options2)) != 5) {
-
- if (k == EOF)
- break;
-
- log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
- goto clean_up;
- }
-
- if (asprintf(&o, "%s,%s", options, options2) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(d = cunescape(device)) ||
- !(p = cunescape(path))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((k = mount_add_one(m, d, p, o, fstype, 0, true, set_flags)) < 0)
- r = k;
-
-clean_up:
- free(device);
- free(path);
- free(options);
- free(options2);
- free(fstype);
- free(d);
- free(p);
- free(o);
- }
-
-finish:
- free(device);
- free(path);
- free(options);
- free(options2);
- free(fstype);
- free(d);
- free(p);
- free(o);
-
- return r;
-}
-
-static void mount_shutdown(Manager *m) {
- assert(m);
-
- if (m->proc_self_mountinfo) {
- fclose(m->proc_self_mountinfo);
- m->proc_self_mountinfo = NULL;
- }
-}
-
-static int mount_enumerate(Manager *m) {
- int r;
- struct epoll_event ev;
- assert(m);
-
- if (!m->proc_self_mountinfo) {
- if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
- return -errno;
-
- m->mount_watch.type = WATCH_MOUNT;
- m->mount_watch.fd = fileno(m->proc_self_mountinfo);
-
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->mount_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
- return -errno;
- }
-
- if ((r = mount_load_etc_fstab(m)) < 0)
- goto fail;
-
- if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
- goto fail;
-
- return 0;
-
-fail:
- mount_shutdown(m);
- return r;
-}
-
-void mount_fd_event(Manager *m, int events) {
- Unit *u;
- int r;
-
- assert(m);
- assert(events & EPOLLPRI);
-
- /* The manager calls this for every fd event happening on the
- * /proc/self/mountinfo file, which informs us about mounting
- * table changes */
-
- if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) {
- log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r));
-
- /* Reset flags, just in case, for later calls */
- LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
- Mount *mount = MOUNT(u);
-
- mount->is_mounted = mount->just_mounted = mount->just_changed = false;
- }
-
- return;
- }
-
- manager_dispatch_load_queue(m);
-
- LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
- Mount *mount = MOUNT(u);
-
- if (!mount->is_mounted) {
- /* This has just been unmounted. */
-
- mount->from_proc_self_mountinfo = false;
-
- switch (mount->state) {
-
- case MOUNT_MOUNTED:
- mount_enter_dead(mount, MOUNT_SUCCESS);
- break;
-
- default:
- mount_set_state(mount, mount->state);
- break;
-
- }
-
- } else if (mount->just_mounted || mount->just_changed) {
-
- /* New or changed mount entry */
-
- switch (mount->state) {
-
- case MOUNT_DEAD:
- case MOUNT_FAILED:
- mount_enter_mounted(mount, MOUNT_SUCCESS);
- break;
-
- case MOUNT_MOUNTING:
- mount_enter_mounting_done(mount);
- break;
-
- default:
- /* Nothing really changed, but let's
- * issue an notification call
- * nonetheless, in case somebody is
- * waiting for this. (e.g. file system
- * ro/rw remounts.) */
- mount_set_state(mount, mount->state);
- break;
- }
- }
-
- /* Reset the flags for later calls */
- mount->is_mounted = mount->just_mounted = mount->just_changed = false;
- }
-}
-
-static void mount_reset_failed(Unit *u) {
- Mount *m = MOUNT(u);
-
- assert(m);
-
- if (m->state == MOUNT_FAILED)
- mount_set_state(m, MOUNT_DEAD);
-
- m->result = MOUNT_SUCCESS;
- m->reload_result = MOUNT_SUCCESS;
-}
-
-static int mount_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
- Mount *m = MOUNT(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(m);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes");
- return -ESRCH;
- }
-
- if (m->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (m->control_pid > 0)
- if (kill(m->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
- int q;
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) {
- r = q;
- goto finish;
- }
-
- if ((q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, pid_set)) < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
-}
-
-static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = "dead",
- [MOUNT_MOUNTING] = "mounting",
- [MOUNT_MOUNTING_DONE] = "mounting-done",
- [MOUNT_MOUNTED] = "mounted",
- [MOUNT_REMOUNTING] = "remounting",
- [MOUNT_UNMOUNTING] = "unmounting",
- [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
- [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
- [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
- [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
- [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
- [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
- [MOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
-
-static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
- [MOUNT_EXEC_MOUNT] = "ExecMount",
- [MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
- [MOUNT_EXEC_REMOUNT] = "ExecRemount",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
-
-static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
- [MOUNT_SUCCESS] = "success",
- [MOUNT_FAILURE_RESOURCES] = "resources",
- [MOUNT_FAILURE_TIMEOUT] = "timeout",
- [MOUNT_FAILURE_EXIT_CODE] = "exit-code",
- [MOUNT_FAILURE_SIGNAL] = "signal",
- [MOUNT_FAILURE_CORE_DUMP] = "core-dump"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
-
-const UnitVTable mount_vtable = {
- .suffix = ".mount",
- .object_size = sizeof(Mount),
- .sections =
- "Unit\0"
- "Mount\0"
- "Install\0",
-
- .no_alias = true,
- .no_instances = true,
- .show_status = true,
-
- .init = mount_init,
- .load = mount_load,
- .done = mount_done,
-
- .coldplug = mount_coldplug,
-
- .dump = mount_dump,
-
- .start = mount_start,
- .stop = mount_stop,
- .reload = mount_reload,
-
- .kill = mount_kill,
-
- .serialize = mount_serialize,
- .deserialize_item = mount_deserialize_item,
-
- .active_state = mount_active_state,
- .sub_state_to_string = mount_sub_state_to_string,
-
- .check_gc = mount_check_gc,
-
- .sigchld_event = mount_sigchld_event,
- .timer_event = mount_timer_event,
-
- .reset_failed = mount_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Mount",
- .bus_message_handler = bus_mount_message_handler,
- .bus_invalidating_properties = bus_mount_invalidating_properties,
-
- .enumerate = mount_enumerate,
- .shutdown = mount_shutdown
-};
diff --git a/src/mount.h b/src/mount.h
deleted file mode 100644
index 9318444..0000000
--- a/src/mount.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foomounthfoo
-#define foomounthfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Mount Mount;
-
-#include "unit.h"
-
-typedef enum MountState {
- MOUNT_DEAD,
- MOUNT_MOUNTING, /* /bin/mount is running, but the mount is not done yet. */
- MOUNT_MOUNTING_DONE, /* /bin/mount is running, and the mount is done. */
- MOUNT_MOUNTED,
- MOUNT_REMOUNTING,
- MOUNT_UNMOUNTING,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL,
- MOUNT_REMOUNTING_SIGTERM,
- MOUNT_REMOUNTING_SIGKILL,
- MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_FAILED,
- _MOUNT_STATE_MAX,
- _MOUNT_STATE_INVALID = -1
-} MountState;
-
-typedef enum MountExecCommand {
- MOUNT_EXEC_MOUNT,
- MOUNT_EXEC_UNMOUNT,
- MOUNT_EXEC_REMOUNT,
- _MOUNT_EXEC_COMMAND_MAX,
- _MOUNT_EXEC_COMMAND_INVALID = -1
-} MountExecCommand;
-
-typedef struct MountParameters {
- char *what;
- char *options;
- char *fstype;
- int passno;
-} MountParameters;
-
-typedef enum MountResult {
- MOUNT_SUCCESS,
- MOUNT_FAILURE_RESOURCES,
- MOUNT_FAILURE_TIMEOUT,
- MOUNT_FAILURE_EXIT_CODE,
- MOUNT_FAILURE_SIGNAL,
- MOUNT_FAILURE_CORE_DUMP,
- _MOUNT_RESULT_MAX,
- _MOUNT_RESULT_INVALID = -1
-} MountResult;
-
-struct Mount {
- Unit meta;
-
- char *where;
-
- MountParameters parameters_etc_fstab;
- MountParameters parameters_proc_self_mountinfo;
- MountParameters parameters_fragment;
-
- bool from_etc_fstab:1;
- bool from_proc_self_mountinfo:1;
- bool from_fragment:1;
-
- /* Used while looking for mount points that vanished or got
- * added from/to /proc/self/mountinfo */
- bool is_mounted:1;
- bool just_mounted:1;
- bool just_changed:1;
-
- MountResult result;
- MountResult reload_result;
-
- mode_t directory_mode;
-
- usec_t timeout_usec;
-
- ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
- ExecContext exec_context;
-
- MountState state, deserialized_state;
-
- ExecCommand* control_command;
- MountExecCommand control_command_id;
- pid_t control_pid;
-
- Watch timer_watch;
-};
-
-extern const UnitVTable mount_vtable;
-
-void mount_fd_event(Manager *m, int events);
-
-const char* mount_state_to_string(MountState i);
-MountState mount_state_from_string(const char *s);
-
-const char* mount_exec_command_to_string(MountExecCommand i);
-MountExecCommand mount_exec_command_from_string(const char *s);
-
-const char* mount_result_to_string(MountResult i);
-MountResult mount_result_from_string(const char *s);
-
-#endif
diff --git a/src/namespace.c b/src/namespace.c
deleted file mode 100644
index 09bc829..0000000
--- a/src/namespace.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <sys/mount.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sched.h>
-#include <sys/syscall.h>
-#include <limits.h>
-#include <linux/fs.h>
-
-#include "strv.h"
-#include "util.h"
-#include "namespace.h"
-#include "missing.h"
-
-typedef enum PathMode {
- /* This is ordered by priority! */
- INACCESSIBLE,
- READONLY,
- PRIVATE,
- READWRITE
-} PathMode;
-
-typedef struct Path {
- const char *path;
- PathMode mode;
-} Path;
-
-static int append_paths(Path **p, char **strv, PathMode mode) {
- char **i;
-
- STRV_FOREACH(i, strv) {
-
- if (!path_is_absolute(*i))
- return -EINVAL;
-
- (*p)->path = *i;
- (*p)->mode = mode;
- (*p)++;
- }
-
- return 0;
-}
-
-static int path_compare(const void *a, const void *b) {
- const Path *p = a, *q = b;
-
- if (path_equal(p->path, q->path)) {
-
- /* If the paths are equal, check the mode */
- if (p->mode < q->mode)
- return -1;
-
- if (p->mode > q->mode)
- return 1;
-
- return 0;
- }
-
- /* If the paths are not equal, then order prefixes first */
- if (path_startswith(p->path, q->path))
- return 1;
-
- if (path_startswith(q->path, p->path))
- return -1;
-
- return 0;
-}
-
-static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
- Path *f, *t, *previous;
-
- assert(p);
- assert(n);
- assert(need_inaccessible);
- assert(need_private);
-
- for (f = p, t = p, previous = NULL; f < p+*n; f++) {
-
- if (previous && path_equal(f->path, previous->path))
- continue;
-
- t->path = f->path;
- t->mode = f->mode;
-
- if (t->mode == PRIVATE)
- *need_private = true;
-
- if (t->mode == INACCESSIBLE)
- *need_inaccessible = true;
-
- previous = t;
-
- t++;
- }
-
- *n = t - p;
-}
-
-static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
- const char *what;
- char *where;
- int r;
-
- assert(p);
- assert(root_dir);
- assert(inaccessible_dir);
- assert(private_dir);
-
- if (!(where = strappend(root_dir, p->path)))
- return -ENOMEM;
-
- switch (p->mode) {
-
- case INACCESSIBLE:
- what = inaccessible_dir;
- flags |= MS_RDONLY;
- break;
-
- case READONLY:
- flags |= MS_RDONLY;
- /* Fall through */
-
- case READWRITE:
- what = p->path;
- break;
-
- case PRIVATE:
- what = private_dir;
- break;
-
- default:
- assert_not_reached("Unknown mode");
- }
-
- if ((r = mount(what, where, NULL, MS_BIND|MS_REC, NULL)) >= 0) {
- log_debug("Successfully mounted %s to %s", what, where);
-
- /* The bind mount will always inherit the original
- * flags. If we want to set any flag we need
- * to do so in a second independent step. */
- if (flags)
- r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
-
- /* Avoid exponential growth of trees */
- if (r >= 0 && path_equal(p->path, "/"))
- r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_UNBINDABLE|flags, NULL);
-
- if (r < 0) {
- r = -errno;
- umount2(where, MNT_DETACH);
- }
- }
-
- free(where);
- return r;
-}
-
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags) {
-
- char
- tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
- root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
- old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
- inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
- private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
-
- Path *paths, *p;
- unsigned n;
- bool need_private = false, need_inaccessible = false;
- bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
- int r;
- const char *t;
-
- n =
- strv_length(writable) +
- strv_length(readable) +
- strv_length(inaccessible) +
- (private_tmp ? 2 : 1);
-
- if (!(paths = new(Path, n)))
- return -ENOMEM;
-
- p = paths;
- if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
- (r = append_paths(&p, readable, READONLY)) < 0 ||
- (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
- goto fail;
-
- if (private_tmp) {
- p->path = "/tmp";
- p->mode = PRIVATE;
- p++;
- }
-
- p->path = "/";
- p->mode = READWRITE;
- p++;
-
- assert(paths + n == p);
-
- qsort(paths, n, sizeof(Path), path_compare);
- drop_duplicates(paths, &n, &need_inaccessible, &need_private);
-
- if (!mkdtemp(tmp_dir)) {
- r = -errno;
- goto fail;
- }
- remove_tmp = true;
-
- memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (mkdir(root_dir, 0777) < 0) {
- r = -errno;
- goto fail;
- }
- remove_root = true;
-
- if (need_inaccessible) {
- memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (mkdir(inaccessible_dir, 0) < 0) {
- r = -errno;
- goto fail;
- }
- remove_inaccessible = true;
- }
-
- if (need_private) {
- mode_t u;
-
- memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
-
- u = umask(0000);
- if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
- umask(u);
-
- r = -errno;
- goto fail;
- }
-
- umask(u);
- remove_private = true;
- }
-
- if (unshare(CLONE_NEWNS) < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Remount / as SLAVE so that nothing mounted in the namespace
- shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- r = -errno;
- goto fail;
- }
-
- for (p = paths; p < paths + n; p++)
- if ((r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags)) < 0)
- goto undo_mounts;
-
- memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (!mkdtemp(old_root_dir)) {
- r = -errno;
- goto undo_mounts;
- }
- remove_old_root = true;
-
- if (chdir(root_dir) < 0) {
- r = -errno;
- goto undo_mounts;
- }
-
- if (pivot_root(root_dir, old_root_dir) < 0) {
- r = -errno;
- goto undo_mounts;
- }
-
- t = old_root_dir + sizeof(root_dir) - 1;
- if (umount2(t, MNT_DETACH) < 0)
- /* At this point it's too late to turn anything back,
- * since we are already in the new root. */
- return -errno;
-
- if (rmdir(t) < 0)
- return -errno;
-
- return 0;
-
-undo_mounts:
-
- for (p--; p >= paths; p--) {
- char full_path[PATH_MAX];
-
- snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
- char_array_0(full_path);
-
- umount2(full_path, MNT_DETACH);
- }
-
-fail:
- if (remove_old_root)
- rmdir(old_root_dir);
-
- if (remove_inaccessible)
- rmdir(inaccessible_dir);
-
- if (remove_private)
- rmdir(private_dir);
-
- if (remove_root)
- rmdir(root_dir);
-
- if (remove_tmp)
- rmdir(tmp_dir);
-
- free(paths);
-
- return r;
-}
diff --git a/src/namespace.h b/src/namespace.h
deleted file mode 100644
index 7cf1ded..0000000
--- a/src/namespace.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foonamespacehfoo
-#define foonamespacehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags);
-
-#endif
diff --git a/src/path.c b/src/path.c
deleted file mode 100644
index 1d50885..0000000
--- a/src/path.c
+++ /dev/null
@@ -1,771 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/inotify.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "unit.h"
-#include "unit-name.h"
-#include "path.h"
-#include "mkdir.h"
-#include "dbus-path.h"
-#include "special.h"
-#include "bus-errors.h"
-
-static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
- [PATH_DEAD] = UNIT_INACTIVE,
- [PATH_WAITING] = UNIT_ACTIVE,
- [PATH_RUNNING] = UNIT_ACTIVE,
- [PATH_FAILED] = UNIT_FAILED
-};
-
-int path_spec_watch(PathSpec *s, Unit *u) {
-
- static const int flags_table[_PATH_TYPE_MAX] = {
- [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
- [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
- [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
- [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY,
- [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
- };
-
- bool exists = false;
- char *k, *slash;
- int r;
-
- assert(u);
- assert(s);
-
- path_spec_unwatch(s, u);
-
- if (!(k = strdup(s->path)))
- return -ENOMEM;
-
- if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
- r = -errno;
- goto fail;
- }
-
- if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) {
- r = -errno;
- goto fail;
- }
-
- if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
- exists = true;
-
- do {
- int flags;
-
- /* This assumes the path was passed through path_kill_slashes()! */
- if (!(slash = strrchr(k, '/')))
- break;
-
- /* Trim the path at the last slash. Keep the slash if it's the root dir. */
- slash[slash == k] = 0;
-
- flags = IN_MOVE_SELF;
- if (!exists)
- flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
-
- if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
- exists = true;
- } while (slash != k);
-
- return 0;
-
-fail:
- free(k);
-
- path_spec_unwatch(s, u);
- return r;
-}
-
-void path_spec_unwatch(PathSpec *s, Unit *u) {
-
- if (s->inotify_fd < 0)
- return;
-
- unit_unwatch_fd(u, &s->watch);
-
- close_nointr_nofail(s->inotify_fd);
- s->inotify_fd = -1;
-}
-
-int path_spec_fd_event(PathSpec *s, uint32_t events) {
- uint8_t *buf = NULL;
- struct inotify_event *e;
- ssize_t k;
- int l;
- int r = 0;
-
- if (events != EPOLLIN) {
- log_error("Got Invalid poll event on inotify.");
- r = -EINVAL;
- goto out;
- }
-
- if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
- log_error("FIONREAD failed: %m");
- r = -errno;
- goto out;
- }
-
- assert(l > 0);
-
- if (!(buf = malloc(l))) {
- log_error("Failed to allocate buffer: %m");
- r = -errno;
- goto out;
- }
-
- if ((k = read(s->inotify_fd, buf, l)) < 0) {
- log_error("Failed to read inotify event: %m");
- r = -errno;
- goto out;
- }
-
- e = (struct inotify_event*) buf;
-
- while (k > 0) {
- size_t step;
-
- if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
- s->primary_wd == e->wd)
- r = 1;
-
- step = sizeof(struct inotify_event) + e->len;
- assert(step <= (size_t) k);
-
- e = (struct inotify_event*) ((uint8_t*) e + step);
- k -= step;
- }
-out:
- free(buf);
- return r;
-}
-
-static bool path_spec_check_good(PathSpec *s, bool initial) {
- bool good = false;
-
- switch (s->type) {
-
- case PATH_EXISTS:
- good = access(s->path, F_OK) >= 0;
- break;
-
- case PATH_EXISTS_GLOB:
- good = glob_exists(s->path) > 0;
- break;
-
- case PATH_DIRECTORY_NOT_EMPTY: {
- int k;
-
- k = dir_is_empty(s->path);
- good = !(k == -ENOENT || k > 0);
- break;
- }
-
- case PATH_CHANGED:
- case PATH_MODIFIED: {
- bool b;
-
- b = access(s->path, F_OK) >= 0;
- good = !initial && b != s->previous_exists;
- s->previous_exists = b;
- break;
- }
-
- default:
- ;
- }
-
- return good;
-}
-
-static bool path_spec_startswith(PathSpec *s, const char *what) {
- return path_startswith(s->path, what);
-}
-
-static void path_spec_mkdir(PathSpec *s, mode_t mode) {
- int r;
-
- if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
- return;
-
- if ((r = mkdir_p(s->path, mode)) < 0)
- log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
-}
-
-static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
- fprintf(f,
- "%s%s: %s\n",
- prefix,
- path_type_to_string(s->type),
- s->path);
-}
-
-void path_spec_done(PathSpec *s) {
- assert(s);
- assert(s->inotify_fd == -1);
-
- free(s->path);
-}
-
-static void path_init(Unit *u) {
- Path *p = PATH(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- p->directory_mode = 0755;
-}
-
-static void path_done(Unit *u) {
- Path *p = PATH(u);
- PathSpec *s;
-
- assert(p);
-
- unit_ref_unset(&p->unit);
-
- while ((s = p->specs)) {
- path_spec_unwatch(s, u);
- LIST_REMOVE(PathSpec, spec, p->specs, s);
- path_spec_done(s);
- free(s);
- }
-}
-
-int path_add_one_mount_link(Path *p, Mount *m) {
- PathSpec *s;
- int r;
-
- assert(p);
- assert(m);
-
- if (UNIT(p)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- LIST_FOREACH(spec, s, p->specs) {
-
- if (!path_spec_startswith(s, m->where))
- continue;
-
- if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int path_add_mount_links(Path *p) {
- Unit *other;
- int r;
-
- assert(p);
-
- LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT])
- if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0)
- return r;
-
- return 0;
-}
-
-static int path_verify(Path *p) {
- assert(p);
-
- if (UNIT(p)->load_state != UNIT_LOADED)
- return 0;
-
- if (!p->specs) {
- log_error("%s lacks path setting. Refusing.", UNIT(p)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int path_add_default_dependencies(Path *p) {
- int r;
-
- assert(p);
-
- if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
- return r;
- }
-
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
-}
-
-static int path_load(Unit *u) {
- Path *p = PATH(u);
- int r;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_LOADED) {
-
- if (!UNIT_DEREF(p->unit)) {
- Unit *x;
-
- r = unit_load_related_unit(u, ".service", &x);
- if (r < 0)
- return r;
-
- unit_ref_set(&p->unit, x);
- }
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
- if (r < 0)
- return r;
-
- if ((r = path_add_mount_links(p)) < 0)
- return r;
-
- if (UNIT(p)->default_dependencies)
- if ((r = path_add_default_dependencies(p)) < 0)
- return r;
- }
-
- return path_verify(p);
-}
-
-static void path_dump(Unit *u, FILE *f, const char *prefix) {
- Path *p = PATH(u);
- PathSpec *s;
-
- assert(p);
- assert(f);
-
- fprintf(f,
- "%sPath State: %s\n"
- "%sResult: %s\n"
- "%sUnit: %s\n"
- "%sMakeDirectory: %s\n"
- "%sDirectoryMode: %04o\n",
- prefix, path_state_to_string(p->state),
- prefix, path_result_to_string(p->result),
- prefix, UNIT_DEREF(p->unit)->id,
- prefix, yes_no(p->make_directory),
- prefix, p->directory_mode);
-
- LIST_FOREACH(spec, s, p->specs)
- path_spec_dump(s, f, prefix);
-}
-
-static void path_unwatch(Path *p) {
- PathSpec *s;
-
- assert(p);
-
- LIST_FOREACH(spec, s, p->specs)
- path_spec_unwatch(s, UNIT(p));
-}
-
-static int path_watch(Path *p) {
- int r;
- PathSpec *s;
-
- assert(p);
-
- LIST_FOREACH(spec, s, p->specs)
- if ((r = path_spec_watch(s, UNIT(p))) < 0)
- return r;
-
- return 0;
-}
-
-static void path_set_state(Path *p, PathState state) {
- PathState old_state;
- assert(p);
-
- old_state = p->state;
- p->state = state;
-
- if (state != PATH_WAITING &&
- (state != PATH_RUNNING || p->inotify_triggered))
- path_unwatch(p);
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(p)->id,
- path_state_to_string(old_state),
- path_state_to_string(state));
-
- unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static void path_enter_waiting(Path *p, bool initial, bool recheck);
-
-static int path_coldplug(Unit *u) {
- Path *p = PATH(u);
-
- assert(p);
- assert(p->state == PATH_DEAD);
-
- if (p->deserialized_state != p->state) {
-
- if (p->deserialized_state == PATH_WAITING ||
- p->deserialized_state == PATH_RUNNING)
- path_enter_waiting(p, true, true);
- else
- path_set_state(p, p->deserialized_state);
- }
-
- return 0;
-}
-
-static void path_enter_dead(Path *p, PathResult f) {
- assert(p);
-
- if (f != PATH_SUCCESS)
- p->result = f;
-
- path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
-}
-
-static void path_enter_running(Path *p) {
- int r;
- DBusError error;
-
- assert(p);
- dbus_error_init(&error);
-
- /* Don't start job if we are supposed to go down */
- if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
- return;
-
- if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
- goto fail;
-
- p->inotify_triggered = false;
-
- if ((r = path_watch(p)) < 0)
- goto fail;
-
- path_set_state(p, PATH_RUNNING);
- return;
-
-fail:
- log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r));
- path_enter_dead(p, PATH_FAILURE_RESOURCES);
-
- dbus_error_free(&error);
-}
-
-static bool path_check_good(Path *p, bool initial) {
- PathSpec *s;
- bool good = false;
-
- assert(p);
-
- LIST_FOREACH(spec, s, p->specs) {
- good = path_spec_check_good(s, initial);
-
- if (good)
- break;
- }
-
- return good;
-}
-
-static void path_enter_waiting(Path *p, bool initial, bool recheck) {
- int r;
-
- if (recheck)
- if (path_check_good(p, initial)) {
- log_debug("%s got triggered.", UNIT(p)->id);
- path_enter_running(p);
- return;
- }
-
- if ((r = path_watch(p)) < 0)
- goto fail;
-
- /* Hmm, so now we have created inotify watches, but the file
- * might have appeared/been removed by now, so we must
- * recheck */
-
- if (recheck)
- if (path_check_good(p, false)) {
- log_debug("%s got triggered.", UNIT(p)->id);
- path_enter_running(p);
- return;
- }
-
- path_set_state(p, PATH_WAITING);
- return;
-
-fail:
- log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r));
- path_enter_dead(p, PATH_FAILURE_RESOURCES);
-}
-
-static void path_mkdir(Path *p) {
- PathSpec *s;
-
- assert(p);
-
- if (!p->make_directory)
- return;
-
- LIST_FOREACH(spec, s, p->specs)
- path_spec_mkdir(s, p->directory_mode);
-}
-
-static int path_start(Unit *u) {
- Path *p = PATH(u);
-
- assert(p);
- assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
-
- if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
- return -ENOENT;
-
- path_mkdir(p);
-
- p->result = PATH_SUCCESS;
- path_enter_waiting(p, true, true);
-
- return 0;
-}
-
-static int path_stop(Unit *u) {
- Path *p = PATH(u);
-
- assert(p);
- assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
-
- path_enter_dead(p, PATH_SUCCESS);
- return 0;
-}
-
-static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
- Path *p = PATH(u);
-
- assert(u);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", path_state_to_string(p->state));
- unit_serialize_item(u, f, "result", path_result_to_string(p->result));
-
- return 0;
-}
-
-static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Path *p = PATH(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- PathState state;
-
- if ((state = path_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- p->deserialized_state = state;
-
- } else if (streq(key, "result")) {
- PathResult f;
-
- f = path_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != PATH_SUCCESS)
- p->result = f;
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState path_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[PATH(u)->state];
-}
-
-static const char *path_sub_state_to_string(Unit *u) {
- assert(u);
-
- return path_state_to_string(PATH(u)->state);
-}
-
-static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
- Path *p = PATH(u);
- PathSpec *s;
- int changed;
-
- assert(p);
- assert(fd >= 0);
-
- if (p->state != PATH_WAITING &&
- p->state != PATH_RUNNING)
- return;
-
- /* log_debug("inotify wakeup on %s.", u->id); */
-
- LIST_FOREACH(spec, s, p->specs)
- if (path_spec_owns_inotify_fd(s, fd))
- break;
-
- if (!s) {
- log_error("Got event on unknown fd.");
- goto fail;
- }
-
- changed = path_spec_fd_event(s, events);
- if (changed < 0)
- goto fail;
-
- /* If we are already running, then remember that one event was
- * dispatched so that we restart the service only if something
- * actually changed on disk */
- p->inotify_triggered = true;
-
- if (changed)
- path_enter_running(p);
- else
- path_enter_waiting(p, false, true);
-
- return;
-
-fail:
- path_enter_dead(p, PATH_FAILURE_RESOURCES);
-}
-
-void path_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_PATH)
- return;
-
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Path *p;
-
- if (k->type != UNIT_PATH)
- continue;
-
- if (k->load_state != UNIT_LOADED)
- continue;
-
- p = PATH(k);
-
- if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
- log_debug("%s got notified about unit deactivation.", UNIT(p)->id);
-
- /* Hmm, so inotify was triggered since the
- * last activation, so I guess we need to
- * recheck what is going on. */
- path_enter_waiting(p, false, p->inotify_triggered);
- }
- }
-}
-
-static void path_reset_failed(Unit *u) {
- Path *p = PATH(u);
-
- assert(p);
-
- if (p->state == PATH_FAILED)
- path_set_state(p, PATH_DEAD);
-
- p->result = PATH_SUCCESS;
-}
-
-static const char* const path_state_table[_PATH_STATE_MAX] = {
- [PATH_DEAD] = "dead",
- [PATH_WAITING] = "waiting",
- [PATH_RUNNING] = "running",
- [PATH_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
-
-static const char* const path_type_table[_PATH_TYPE_MAX] = {
- [PATH_EXISTS] = "PathExists",
- [PATH_EXISTS_GLOB] = "PathExistsGlob",
- [PATH_CHANGED] = "PathChanged",
- [PATH_MODIFIED] = "PathModified",
- [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
-
-static const char* const path_result_table[_PATH_RESULT_MAX] = {
- [PATH_SUCCESS] = "success",
- [PATH_FAILURE_RESOURCES] = "resources"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
-
-const UnitVTable path_vtable = {
- .suffix = ".path",
- .object_size = sizeof(Path),
- .sections =
- "Unit\0"
- "Path\0"
- "Install\0",
-
- .init = path_init,
- .done = path_done,
- .load = path_load,
-
- .coldplug = path_coldplug,
-
- .dump = path_dump,
-
- .start = path_start,
- .stop = path_stop,
-
- .serialize = path_serialize,
- .deserialize_item = path_deserialize_item,
-
- .active_state = path_active_state,
- .sub_state_to_string = path_sub_state_to_string,
-
- .fd_event = path_fd_event,
-
- .reset_failed = path_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Path",
- .bus_message_handler = bus_path_message_handler,
- .bus_invalidating_properties = bus_path_invalidating_properties
-};
diff --git a/src/path.h b/src/path.h
deleted file mode 100644
index efb6b5e..0000000
--- a/src/path.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foopathhfoo
-#define foopathhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Path Path;
-
-#include "unit.h"
-#include "mount.h"
-
-typedef enum PathState {
- PATH_DEAD,
- PATH_WAITING,
- PATH_RUNNING,
- PATH_FAILED,
- _PATH_STATE_MAX,
- _PATH_STATE_INVALID = -1
-} PathState;
-
-typedef enum PathType {
- PATH_EXISTS,
- PATH_EXISTS_GLOB,
- PATH_DIRECTORY_NOT_EMPTY,
- PATH_CHANGED,
- PATH_MODIFIED,
- _PATH_TYPE_MAX,
- _PATH_TYPE_INVALID = -1
-} PathType;
-
-typedef struct PathSpec {
- char *path;
-
- Watch watch;
-
- LIST_FIELDS(struct PathSpec, spec);
-
- PathType type;
- int inotify_fd;
- int primary_wd;
-
- bool previous_exists;
-} PathSpec;
-
-int path_spec_watch(PathSpec *s, Unit *u);
-void path_spec_unwatch(PathSpec *s, Unit *u);
-int path_spec_fd_event(PathSpec *s, uint32_t events);
-void path_spec_done(PathSpec *s);
-
-static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
- return s->inotify_fd == fd;
-}
-
-typedef enum PathResult {
- PATH_SUCCESS,
- PATH_FAILURE_RESOURCES,
- _PATH_RESULT_MAX,
- _PATH_RESULT_INVALID = -1
-} PathResult;
-
-struct Path {
- Unit meta;
-
- LIST_HEAD(PathSpec, specs);
-
- UnitRef unit;
-
- PathState state, deserialized_state;
-
- bool inotify_triggered;
-
- bool make_directory;
- mode_t directory_mode;
-
- PathResult result;
-};
-
-void path_unit_notify(Unit *u, UnitActiveState new_state);
-
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the paths of this path object */
-int path_add_one_mount_link(Path *p, Mount *m);
-
-extern const UnitVTable path_vtable;
-
-const char* path_state_to_string(PathState i);
-PathState path_state_from_string(const char *s);
-
-const char* path_type_to_string(PathType i);
-PathType path_type_from_string(const char *s);
-
-const char* path_result_to_string(PathResult i);
-PathResult path_result_from_string(const char *s);
-
-#endif
diff --git a/src/polkit.h b/src/polkit.h
deleted file mode 100644
index 0d08194..0000000
--- a/src/polkit.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foopolkithfoo
-#define foopolkithfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <dbus/dbus.h>
-
-int verify_polkit(
- DBusConnection *c,
- DBusMessage *request,
- const char *action,
- bool interactive,
- bool *challenge,
- DBusError *error);
-
-#endif
diff --git a/src/securebits.h b/src/securebits.h
deleted file mode 100644
index ba0bba5..0000000
--- a/src/securebits.h
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _LINUX_SECUREBITS_H
-#define _LINUX_SECUREBITS_H 1
-
-/* This is minimal version of Linux' linux/securebits.h header file,
- * which is licensed GPL2 */
-
-#define SECUREBITS_DEFAULT 0x00000000
-
-/* When set UID 0 has no special privileges. When unset, we support
- inheritance of root-permissions and suid-root executable under
- compatibility mode. We raise the effective and inheritable bitmasks
- *of the executable file* if the effective uid of the new process is
- 0. If the real uid is 0, we raise the effective (legacy) bit of the
- executable file. */
-#define SECURE_NOROOT 0
-#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */
-
-/* When set, setuid to/from uid 0 does not trigger capability-"fixup".
- When unset, to provide compatibility with old programs relying on
- set*uid to gain/lose privilege, transitions to/from uid 0 cause
- capabilities to be gained/lost. */
-#define SECURE_NO_SETUID_FIXUP 2
-#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */
-
-/* When set, a process can retain its capabilities even after
- transitioning to a non-root user (the set-uid fixup suppressed by
- bit 2). Bit-4 is cleared when a process calls exec(); setting both
- bit 4 and 5 will create a barrier through exec that no exec()'d
- child can use this feature again. */
-#define SECURE_KEEP_CAPS 4
-#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */
-
-/* Each securesetting is implemented using two bits. One bit specifies
- whether the setting is on or off. The other bit specify whether the
- setting is locked or not. A setting which is locked cannot be
- changed from user-level. */
-#define issecure_mask(X) (1 << (X))
-#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits))
-
-#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
- issecure_mask(SECURE_NO_SETUID_FIXUP) | \
- issecure_mask(SECURE_KEEP_CAPS))
-#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
-
-#endif /* !_LINUX_SECUREBITS_H */
diff --git a/src/selinux-setup.c b/src/selinux-setup.c
deleted file mode 100644
index a7e1fa4..0000000
--- a/src/selinux-setup.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
-#include "selinux-setup.h"
-#include "mount-setup.h"
-#include "macro.h"
-#include "util.h"
-#include "log.h"
-#include "label.h"
-
-int selinux_setup(bool *loaded_policy) {
-
-#ifdef HAVE_SELINUX
- int enforce = 0;
- usec_t before_load, after_load;
- security_context_t con;
- int r;
-
- assert(loaded_policy);
-
- /* Make sure getcon() works, which needs /proc and /sys */
- mount_setup_early();
-
- /* Already initialized by somebody else? */
- r = getcon_raw(&con);
- if (r == 0) {
- bool initialized;
-
- initialized = !streq(con, "kernel");
- freecon(con);
-
- if (initialized)
- return 0;
- }
-
- /* Make sure we have no fds open while loading the policy and
- * transitioning */
- log_close();
-
- /* Now load the policy */
- before_load = now(CLOCK_MONOTONIC);
- r = selinux_init_load_policy(&enforce);
-
- if (r == 0) {
- char timespan[FORMAT_TIMESPAN_MAX];
- char *label;
-
- label_retest_selinux();
-
- /* Transition to the new context */
- r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
- if (r < 0 || label == NULL) {
- log_open();
- log_error("Failed to compute init label, ignoring.");
- } else {
- r = setcon(label);
-
- log_open();
- if (r < 0)
- log_error("Failed to transition into init label '%s', ignoring.", label);
-
- label_free(label);
- }
-
- after_load = now(CLOCK_MONOTONIC);
-
- log_info("Successfully loaded SELinux policy in %s.",
- format_timespan(timespan, sizeof(timespan), after_load - before_load));
-
- *loaded_policy = true;
-
- } else {
- log_open();
-
- if (enforce > 0) {
- log_error("Failed to load SELinux policy. Freezing.");
- return -EIO;
- } else
- log_debug("Unable to load SELinux policy. Ignoring.");
- }
-#endif
-
- return 0;
-}
diff --git a/src/selinux-setup.h b/src/selinux-setup.h
deleted file mode 100644
index 6b8fe00..0000000
--- a/src/selinux-setup.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooselinuxsetuphfoo
-#define fooselinuxsetuphfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-int selinux_setup(bool *loaded_policy);
-
-#endif
diff --git a/src/service.c b/src/service.c
deleted file mode 100644
index bf2e0a9..0000000
--- a/src/service.c
+++ /dev/null
@@ -1,3797 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <signal.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <sys/reboot.h>
-
-#include "manager.h"
-#include "unit.h"
-#include "service.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "unit-name.h"
-#include "dbus-service.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "exit-status.h"
-#include "def.h"
-#include "util.h"
-#include "utf8.h"
-
-#ifdef HAVE_SYSV_COMPAT
-
-#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE)
-
-typedef enum RunlevelType {
- RUNLEVEL_UP,
- RUNLEVEL_DOWN,
- RUNLEVEL_SYSINIT
-} RunlevelType;
-
-static const struct {
- const char *path;
- const char *target;
- const RunlevelType type;
-} rcnd_table[] = {
- /* Standard SysV runlevels for start-up */
- { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP },
- { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP },
- { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP },
- { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
- { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
-
-#ifdef TARGET_SUSE
- /* SUSE style boot.d */
- { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
-#endif
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- /* Debian style rcS.d */
- { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
-#endif
-
- /* Standard SysV runlevels for shutdown */
- { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
- { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
-
- /* Note that the order here matters, as we read the
- directories in this order, and we want to make sure that
- sysv_start_priority is known when we first load the
- unit. And that value we only know from S links. Hence
- UP/SYSINIT must be read before DOWN */
-};
-
-#define RUNLEVELS_UP "12345"
-/* #define RUNLEVELS_DOWN "06" */
-#define RUNLEVELS_BOOT "bBsS"
-#endif
-
-static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = UNIT_INACTIVE,
- [SERVICE_START_PRE] = UNIT_ACTIVATING,
- [SERVICE_START] = UNIT_ACTIVATING,
- [SERVICE_START_POST] = UNIT_ACTIVATING,
- [SERVICE_RUNNING] = UNIT_ACTIVE,
- [SERVICE_EXITED] = UNIT_ACTIVE,
- [SERVICE_RELOAD] = UNIT_RELOADING,
- [SERVICE_STOP] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
-};
-
-static void service_init(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
- s->restart_usec = DEFAULT_RESTART_USEC;
-
- s->watchdog_watch.type = WATCH_INVALID;
-
- s->timer_watch.type = WATCH_INVALID;
-#ifdef HAVE_SYSV_COMPAT
- s->sysv_start_priority = -1;
- s->sysv_start_priority_from_rcnd = -1;
-#endif
- s->socket_fd = -1;
- s->guess_main_pid = true;
-
- exec_context_init(&s->exec_context);
-
- RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
-
- s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
-}
-
-static void service_unwatch_control_pid(Service *s) {
- assert(s);
-
- if (s->control_pid <= 0)
- return;
-
- unit_unwatch_pid(UNIT(s), s->control_pid);
- s->control_pid = 0;
-}
-
-static void service_unwatch_main_pid(Service *s) {
- assert(s);
-
- if (s->main_pid <= 0)
- return;
-
- unit_unwatch_pid(UNIT(s), s->main_pid);
- s->main_pid = 0;
-}
-
-static void service_unwatch_pid_file(Service *s) {
- if (!s->pid_file_pathspec)
- return;
-
- log_debug("Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
- path_spec_unwatch(s->pid_file_pathspec, UNIT(s));
- path_spec_done(s->pid_file_pathspec);
- free(s->pid_file_pathspec);
- s->pid_file_pathspec = NULL;
-}
-
-static int service_set_main_pid(Service *s, pid_t pid) {
- pid_t ppid;
-
- assert(s);
-
- if (pid <= 1)
- return -EINVAL;
-
- if (pid == getpid())
- return -EINVAL;
-
- s->main_pid = pid;
- s->main_pid_known = true;
-
- if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
- log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
- UNIT(s)->id, (unsigned long) pid);
-
- s->main_pid_alien = true;
- } else
- s->main_pid_alien = false;
-
- exec_status_start(&s->main_exec_status, pid);
-
- return 0;
-}
-
-static void service_close_socket_fd(Service *s) {
- assert(s);
-
- if (s->socket_fd < 0)
- return;
-
- close_nointr_nofail(s->socket_fd);
- s->socket_fd = -1;
-}
-
-static void service_connection_unref(Service *s) {
- assert(s);
-
- if (!UNIT_DEREF(s->accept_socket))
- return;
-
- socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
- unit_ref_unset(&s->accept_socket);
-}
-
-static void service_stop_watchdog(Service *s) {
- assert(s);
-
- unit_unwatch_timer(UNIT(s), &s->watchdog_watch);
- s->watchdog_timestamp.realtime = 0;
- s->watchdog_timestamp.monotonic = 0;
-}
-
-static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart);
-
-static void service_handle_watchdog(Service *s) {
- usec_t offset;
- int r;
-
- assert(s);
-
- if (s->watchdog_usec == 0)
- return;
-
- offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
- if (offset >= s->watchdog_usec) {
- log_error("%s watchdog timeout!", UNIT(s)->id);
- service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true);
- return;
- }
-
- r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch);
- if (r < 0)
- log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r));
-}
-
-static void service_reset_watchdog(Service *s) {
- assert(s);
-
- dual_timestamp_get(&s->watchdog_timestamp);
- service_handle_watchdog(s);
-}
-
-static void service_done(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- free(s->pid_file);
- s->pid_file = NULL;
-
-#ifdef HAVE_SYSV_COMPAT
- free(s->sysv_path);
- s->sysv_path = NULL;
-
- free(s->sysv_runlevels);
- s->sysv_runlevels = NULL;
-#endif
-
- free(s->status_text);
- s->status_text = NULL;
-
- exec_context_done(&s->exec_context);
- exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
- s->control_command = NULL;
- s->main_command = NULL;
-
- /* This will leak a process, but at least no memory or any of
- * our resources */
- service_unwatch_main_pid(s);
- service_unwatch_control_pid(s);
- service_unwatch_pid_file(s);
-
- if (s->bus_name) {
- unit_unwatch_bus_name(u, s->bus_name);
- free(s->bus_name);
- s->bus_name = NULL;
- }
-
- service_close_socket_fd(s);
- service_connection_unref(s);
-
- unit_ref_unset(&s->accept_socket);
-
- service_stop_watchdog(s);
-
- unit_unwatch_timer(u, &s->timer_watch);
-}
-
-#ifdef HAVE_SYSV_COMPAT
-static char *sysv_translate_name(const char *name) {
- char *r;
-
- if (!(r = new(char, strlen(name) + sizeof(".service"))))
- return NULL;
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (endswith(name, ".sh"))
- /* Drop Debian-style .sh suffix */
- strcpy(stpcpy(r, name) - 3, ".service");
-#endif
-#ifdef TARGET_SUSE
- if (startswith(name, "boot."))
- /* Drop SuSE-style boot. prefix */
- strcpy(stpcpy(r, name + 5), ".service");
-#endif
-#ifdef TARGET_FRUGALWARE
- if (startswith(name, "rc."))
- /* Drop Frugalware-style rc. prefix */
- strcpy(stpcpy(r, name + 3), ".service");
-#endif
- else
- /* Normal init scripts */
- strcpy(stpcpy(r, name), ".service");
-
- return r;
-}
-
-static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
-
- /* We silently ignore the $ prefix here. According to the LSB
- * spec it simply indicates whether something is a
- * standardized name or a distribution-specific one. Since we
- * just follow what already exists and do not introduce new
- * uses or names we don't care who introduced a new name. */
-
- static const char * const table[] = {
- /* LSB defined facilities */
- "local_fs", SPECIAL_LOCAL_FS_TARGET,
-#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-#else
- /* Due to unfortunate name selection in Mandriva,
- * $network is provided by network-up which is ordered
- * after network which actually starts interfaces.
- * To break the loop, just ignore it */
- "network", SPECIAL_NETWORK_TARGET,
-#endif
- "named", SPECIAL_NSS_LOOKUP_TARGET,
- "portmap", SPECIAL_RPCBIND_TARGET,
- "remote_fs", SPECIAL_REMOTE_FS_TARGET,
- "syslog", SPECIAL_SYSLOG_TARGET,
- "time", SPECIAL_TIME_SYNC_TARGET,
-
- /* common extensions */
- "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
- "null", NULL,
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-#endif
-
-#ifdef TARGET_FEDORA
- "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "httpd", SPECIAL_HTTP_DAEMON_TARGET,
-#endif
-
-#ifdef TARGET_SUSE
- "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-#endif
- };
-
- unsigned i;
- char *r;
- const char *n;
-
- assert(name);
- assert(_r);
-
- n = *name == '$' ? name + 1 : name;
-
- for (i = 0; i < ELEMENTSOF(table); i += 2) {
-
- if (!streq(table[i], n))
- continue;
-
- if (!table[i+1])
- return 0;
-
- if (!(r = strdup(table[i+1])))
- return -ENOMEM;
-
- goto finish;
- }
-
- /* If we don't know this name, fallback heuristics to figure
- * out whether something is a target or a service alias. */
-
- if (*name == '$') {
- if (!unit_prefix_is_valid(n))
- return -EINVAL;
-
- /* Facilities starting with $ are most likely targets */
- r = unit_name_build(n, NULL, ".target");
- } else if (filename && streq(name, filename))
- /* Names equaling the file name of the services are redundant */
- return 0;
- else
- /* Everything else we assume to be normal service names */
- r = sysv_translate_name(n);
-
- if (!r)
- return -ENOMEM;
-
-finish:
- *_r = r;
-
- return 1;
-}
-
-static int sysv_fix_order(Service *s) {
- Unit *other;
- int r;
-
- assert(s);
-
- if (s->sysv_start_priority < 0)
- return 0;
-
- /* For each pair of services where at least one lacks a LSB
- * header, we use the start priority value to order things. */
-
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) {
- Service *t;
- UnitDependency d;
- bool special_s, special_t;
-
- t = SERVICE(other);
-
- if (s == t)
- continue;
-
- if (UNIT(t)->load_state != UNIT_LOADED)
- continue;
-
- if (t->sysv_start_priority < 0)
- continue;
-
- /* If both units have modern headers we don't care
- * about the priorities */
- if ((UNIT(s)->fragment_path || s->sysv_has_lsb) &&
- (UNIT(t)->fragment_path || t->sysv_has_lsb))
- continue;
-
- special_s = s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels);
- special_t = t->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, t->sysv_runlevels);
-
- if (special_t && !special_s)
- d = UNIT_AFTER;
- else if (special_s && !special_t)
- d = UNIT_BEFORE;
- else if (t->sysv_start_priority < s->sysv_start_priority)
- d = UNIT_AFTER;
- else if (t->sysv_start_priority > s->sysv_start_priority)
- d = UNIT_BEFORE;
- else
- continue;
-
- /* FIXME: Maybe we should compare the name here lexicographically? */
-
- if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static ExecCommand *exec_command_new(const char *path, const char *arg1) {
- ExecCommand *c;
-
- if (!(c = new0(ExecCommand, 1)))
- return NULL;
-
- if (!(c->path = strdup(path))) {
- free(c);
- return NULL;
- }
-
- if (!(c->argv = strv_new(path, arg1, NULL))) {
- free(c->path);
- free(c);
- return NULL;
- }
-
- return c;
-}
-
-static int sysv_exec_commands(Service *s) {
- ExecCommand *c;
-
- assert(s);
- assert(s->sysv_path);
-
- if (!(c = exec_command_new(s->sysv_path, "start")))
- return -ENOMEM;
- exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c);
-
- if (!(c = exec_command_new(s->sysv_path, "stop")))
- return -ENOMEM;
- exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c);
-
- if (!(c = exec_command_new(s->sysv_path, "reload")))
- return -ENOMEM;
- exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
-
- return 0;
-}
-
-static int service_load_sysv_path(Service *s, const char *path) {
- FILE *f;
- Unit *u;
- unsigned line = 0;
- int r;
- enum {
- NORMAL,
- DESCRIPTION,
- LSB,
- LSB_DESCRIPTION
- } state = NORMAL;
- char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
- struct stat st;
-
- assert(s);
- assert(path);
-
- u = UNIT(s);
-
- if (!(f = fopen(path, "re"))) {
- r = errno == ENOENT ? 0 : -errno;
- goto finish;
- }
-
- zero(st);
- if (fstat(fileno(f), &st) < 0) {
- r = -errno;
- goto finish;
- }
-
- free(s->sysv_path);
- if (!(s->sysv_path = strdup(path))) {
- r = -ENOMEM;
- goto finish;
- }
-
- s->sysv_mtime = timespec_load(&st.st_mtim);
-
- if (null_or_empty(&st)) {
- u->load_state = UNIT_MASKED;
- r = 0;
- goto finish;
- }
-
- while (!feof(f)) {
- char l[LINE_MAX], *t;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- log_error("Failed to read configuration file '%s': %s", path, strerror(-r));
- goto finish;
- }
-
- line++;
-
- t = strstrip(l);
- if (*t != '#')
- continue;
-
- if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
- state = LSB;
- s->sysv_has_lsb = true;
- continue;
- }
-
- if ((state == LSB_DESCRIPTION || state == LSB) && streq(t, "### END INIT INFO")) {
- state = NORMAL;
- continue;
- }
-
- t++;
- t += strspn(t, WHITESPACE);
-
- if (state == NORMAL) {
-
- /* Try to parse Red Hat style chkconfig headers */
-
- if (startswith_no_case(t, "chkconfig:")) {
- int start_priority;
- char runlevels[16], *k;
-
- state = NORMAL;
-
- if (sscanf(t+10, "%15s %i %*i",
- runlevels,
- &start_priority) != 2) {
-
- log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line);
- continue;
- }
-
- /* A start priority gathered from the
- * symlink farms is preferred over the
- * data from the LSB header. */
- if (start_priority < 0 || start_priority > 99)
- log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line);
- else
- s->sysv_start_priority = start_priority;
-
- char_array_0(runlevels);
- k = delete_chars(runlevels, WHITESPACE "-");
-
- if (k[0]) {
- char *d;
-
- if (!(d = strdup(k))) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(s->sysv_runlevels);
- s->sysv_runlevels = d;
- }
-
- } else if (startswith_no_case(t, "description:")) {
-
- size_t k = strlen(t);
- char *d;
- const char *j;
-
- if (t[k-1] == '\\') {
- state = DESCRIPTION;
- t[k-1] = 0;
- }
-
- if ((j = strstrip(t+12)) && *j) {
- if (!(d = strdup(j))) {
- r = -ENOMEM;
- goto finish;
- }
- } else
- d = NULL;
-
- free(chkconfig_description);
- chkconfig_description = d;
-
- } else if (startswith_no_case(t, "pidfile:")) {
-
- char *fn;
-
- state = NORMAL;
-
- fn = strstrip(t+8);
- if (!path_is_absolute(fn)) {
- log_warning("[%s:%u] PID file not absolute. Ignoring.", path, line);
- continue;
- }
-
- if (!(fn = strdup(fn))) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(s->pid_file);
- s->pid_file = fn;
- }
-
- } else if (state == DESCRIPTION) {
-
- /* Try to parse Red Hat style description
- * continuation */
-
- size_t k = strlen(t);
- char *j;
-
- if (t[k-1] == '\\')
- t[k-1] = 0;
- else
- state = NORMAL;
-
- if ((j = strstrip(t)) && *j) {
- char *d = NULL;
-
- if (chkconfig_description)
- d = join(chkconfig_description, " ", j, NULL);
- else
- d = strdup(j);
-
- if (!d) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(chkconfig_description);
- chkconfig_description = d;
- }
-
- } else if (state == LSB || state == LSB_DESCRIPTION) {
-
- if (startswith_no_case(t, "Provides:")) {
- char *i, *w;
- size_t z;
-
- state = LSB;
-
- FOREACH_WORD_QUOTED(w, z, t+9, i) {
- char *n, *m;
-
- if (!(n = strndup(w, z))) {
- r = -ENOMEM;
- goto finish;
- }
-
- r = sysv_translate_facility(n, file_name_from_path(path), &m);
- free(n);
-
- if (r < 0)
- goto finish;
-
- if (r == 0)
- continue;
-
- if (unit_name_to_type(m) == UNIT_SERVICE)
- r = unit_add_name(u, m);
- else
- /* NB: SysV targets
- * which are provided
- * by a service are
- * pulled in by the
- * services, as an
- * indication that the
- * generic service is
- * now available. This
- * is strictly
- * one-way. The
- * targets do NOT pull
- * in the SysV
- * services! */
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true);
-
- if (r < 0)
- log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r));
-
- free(m);
- }
-
- } else if (startswith_no_case(t, "Required-Start:") ||
- startswith_no_case(t, "Should-Start:") ||
- startswith_no_case(t, "X-Start-Before:") ||
- startswith_no_case(t, "X-Start-After:")) {
- char *i, *w;
- size_t z;
-
- state = LSB;
-
- FOREACH_WORD_QUOTED(w, z, strchr(t, ':')+1, i) {
- char *n, *m;
-
- if (!(n = strndup(w, z))) {
- r = -ENOMEM;
- goto finish;
- }
-
- r = sysv_translate_facility(n, file_name_from_path(path), &m);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r));
- free(n);
- continue;
- }
-
- free(n);
-
- if (r == 0)
- continue;
-
- r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
-
- if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", path, line, m, strerror(-r));
-
- free(m);
- }
- } else if (startswith_no_case(t, "Default-Start:")) {
- char *k, *d;
-
- state = LSB;
-
- k = delete_chars(t+14, WHITESPACE "-");
-
- if (k[0] != 0) {
- if (!(d = strdup(k))) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(s->sysv_runlevels);
- s->sysv_runlevels = d;
- }
-
- } else if (startswith_no_case(t, "Description:")) {
- char *d, *j;
-
- state = LSB_DESCRIPTION;
-
- if ((j = strstrip(t+12)) && *j) {
- if (!(d = strdup(j))) {
- r = -ENOMEM;
- goto finish;
- }
- } else
- d = NULL;
-
- free(long_description);
- long_description = d;
-
- } else if (startswith_no_case(t, "Short-Description:")) {
- char *d, *j;
-
- state = LSB;
-
- if ((j = strstrip(t+18)) && *j) {
- if (!(d = strdup(j))) {
- r = -ENOMEM;
- goto finish;
- }
- } else
- d = NULL;
-
- free(short_description);
- short_description = d;
-
- } else if (state == LSB_DESCRIPTION) {
-
- if (startswith(l, "#\t") || startswith(l, "# ")) {
- char *j;
-
- if ((j = strstrip(t)) && *j) {
- char *d = NULL;
-
- if (long_description)
- d = join(long_description, " ", t, NULL);
- else
- d = strdup(j);
-
- if (!d) {
- r = -ENOMEM;
- goto finish;
- }
-
- free(long_description);
- long_description = d;
- }
-
- } else
- state = LSB;
- }
- }
- }
-
- if ((r = sysv_exec_commands(s)) < 0)
- goto finish;
- if (s->sysv_runlevels &&
- chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) &&
- chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
- /* Service has both boot and "up" runlevels
- configured. Kill the "up" ones. */
- delete_chars(s->sysv_runlevels, RUNLEVELS_UP);
- }
-
- if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
- /* If there a runlevels configured for this service
- * but none of the standard ones, then we assume this
- * is some special kind of service (which might be
- * needed for early boot) and don't create any links
- * to it. */
-
- UNIT(s)->default_dependencies = false;
-
- /* Don't timeout special services during boot (like fsck) */
- s->timeout_usec = 0;
- } else
- s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
-
- /* Special setting for all SysV services */
- s->type = SERVICE_FORKING;
- s->remain_after_exit = !s->pid_file;
- s->guess_main_pid = false;
- s->restart = SERVICE_RESTART_NO;
- s->exec_context.ignore_sigpipe = false;
-
- if (UNIT(s)->manager->sysv_console)
- s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
-
- s->exec_context.kill_mode = KILL_PROCESS;
-
- /* We use the long description only if
- * no short description is set. */
-
- if (short_description)
- description = short_description;
- else if (chkconfig_description)
- description = chkconfig_description;
- else if (long_description)
- description = long_description;
- else
- description = NULL;
-
- if (description) {
- char *d;
-
- if (!(d = strappend(s->sysv_has_lsb ? "LSB: " : "SYSV: ", description))) {
- r = -ENOMEM;
- goto finish;
- }
-
- u->description = d;
- }
-
- /* The priority that has been set in /etc/rcN.d/ hierarchies
- * takes precedence over what is stored as default in the LSB
- * header */
- if (s->sysv_start_priority_from_rcnd >= 0)
- s->sysv_start_priority = s->sysv_start_priority_from_rcnd;
-
- u->load_state = UNIT_LOADED;
- r = 0;
-
-finish:
-
- if (f)
- fclose(f);
-
- free(short_description);
- free(long_description);
- free(chkconfig_description);
-
- return r;
-}
-
-static int service_load_sysv_name(Service *s, const char *name) {
- char **p;
-
- assert(s);
- assert(name);
-
- /* For SysV services we strip the boot.*, rc.* and *.sh
- * prefixes/suffixes. */
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (endswith(name, ".sh.service"))
- return -ENOENT;
-#endif
-
-#ifdef TARGET_SUSE
- if (startswith(name, "boot."))
- return -ENOENT;
-#endif
-
-#ifdef TARGET_FRUGALWARE
- if (startswith(name, "rc."))
- return -ENOENT;
-#endif
-
- STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
- char *path;
- int r;
-
- path = join(*p, "/", name, NULL);
- if (!path)
- return -ENOMEM;
-
- assert(endswith(path, ".service"));
- path[strlen(path)-8] = 0;
-
- r = service_load_sysv_path(s, path);
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Debian style *.sh source'able init scripts */
- strcat(path, ".sh");
- r = service_load_sysv_path(s, path);
- }
-#endif
- free(path);
-
-#ifdef TARGET_SUSE
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try SUSE style boot.* init scripts */
-
- path = join(*p, "/boot.", name, NULL);
- if (!path)
- return -ENOMEM;
-
- /* Drop .service suffix */
- path[strlen(path)-8] = 0;
- r = service_load_sysv_path(s, path);
- free(path);
- }
-#endif
-
-#ifdef TARGET_FRUGALWARE
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Frugalware style rc.* init scripts */
-
- path = join(*p, "/rc.", name, NULL);
- if (!path)
- return -ENOMEM;
-
- /* Drop .service suffix */
- path[strlen(path)-8] = 0;
- r = service_load_sysv_path(s, path);
- free(path);
- }
-#endif
-
- if (r < 0)
- return r;
-
- if ((UNIT(s)->load_state != UNIT_STUB))
- break;
- }
-
- return 0;
-}
-
-static int service_load_sysv(Service *s) {
- const char *t;
- Iterator i;
- int r;
-
- assert(s);
-
- /* Load service data from SysV init scripts, preferably with
- * LSB headers ... */
-
- if (strv_isempty(UNIT(s)->manager->lookup_paths.sysvinit_path))
- return 0;
-
- if ((t = UNIT(s)->id))
- if ((r = service_load_sysv_name(s, t)) < 0)
- return r;
-
- if (UNIT(s)->load_state == UNIT_STUB)
- SET_FOREACH(t, UNIT(s)->names, i) {
- if (t == UNIT(s)->id)
- continue;
-
- if ((r = service_load_sysv_name(s, t)) < 0)
- return r;
-
- if (UNIT(s)->load_state != UNIT_STUB)
- break;
- }
-
- return 0;
-}
-#endif
-
-static int fsck_fix_order(Service *s) {
- Unit *other;
- int r;
-
- assert(s);
-
- if (s->fsck_passno <= 0)
- return 0;
-
- /* For each pair of services where both have an fsck priority
- * we order things based on it. */
-
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_SERVICE]) {
- Service *t;
- UnitDependency d;
-
- t = SERVICE(other);
-
- if (s == t)
- continue;
-
- if (UNIT(t)->load_state != UNIT_LOADED)
- continue;
-
- if (t->fsck_passno <= 0)
- continue;
-
- if (t->fsck_passno < s->fsck_passno)
- d = UNIT_AFTER;
- else if (t->fsck_passno > s->fsck_passno)
- d = UNIT_BEFORE;
- else
- continue;
-
- if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int service_verify(Service *s) {
- assert(s);
-
- if (UNIT(s)->load_state != UNIT_LOADED)
- return 0;
-
- if (!s->exec_command[SERVICE_EXEC_START]) {
- log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->type != SERVICE_ONESHOT &&
- s->exec_command[SERVICE_EXEC_START]->command_next) {
- log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->type == SERVICE_ONESHOT &&
- s->exec_command[SERVICE_EXEC_RELOAD]) {
- log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->type == SERVICE_DBUS && !s->bus_name) {
- log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int service_add_default_dependencies(Service *s) {
- int r;
-
- assert(s);
-
- /* Add a number of automatic dependencies useful for the
- * majority of services. */
-
- /* First, pull in base system */
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
-
- } else if (UNIT(s)->manager->running_as == MANAGER_USER) {
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
- return r;
- }
-
- /* Second, activate normal shutdown */
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
-}
-
-static void service_fix_output(Service *s) {
- assert(s);
-
- /* If nothing has been explicitly configured, patch default
- * output in. If input is socket/tty we avoid this however,
- * since in that case we want output to default to the same
- * place as we read input from. */
-
- if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_input == EXEC_INPUT_NULL)
- s->exec_context.std_error = UNIT(s)->manager->default_std_error;
-
- if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
- s->exec_context.std_input == EXEC_INPUT_NULL)
- s->exec_context.std_output = UNIT(s)->manager->default_std_output;
-}
-
-static int service_load(Unit *u) {
- int r;
- Service *s = SERVICE(u);
-
- assert(s);
-
- /* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
- return r;
-
-#ifdef HAVE_SYSV_COMPAT
- /* Load a classic init script as a fallback, if we couldn't find anything */
- if (u->load_state == UNIT_STUB)
- if ((r = service_load_sysv(s)) < 0)
- return r;
-#endif
-
- /* Still nothing found? Then let's give up */
- if (u->load_state == UNIT_STUB)
- return -ENOENT;
-
- /* We were able to load something, then let's add in the
- * dropin directories. */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
- return r;
-
- /* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
- service_fix_output(s);
-
- if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
- return r;
-
- if ((r = unit_add_default_cgroups(u)) < 0)
- return r;
-
-#ifdef HAVE_SYSV_COMPAT
- if ((r = sysv_fix_order(s)) < 0)
- return r;
-#endif
-
- if ((r = fsck_fix_order(s)) < 0)
- return r;
-
- if (s->bus_name)
- if ((r = unit_watch_bus_name(u, s->bus_name)) < 0)
- return r;
-
- if (s->type == SERVICE_NOTIFY && s->notify_access == NOTIFY_NONE)
- s->notify_access = NOTIFY_MAIN;
-
- if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
- s->notify_access = NOTIFY_MAIN;
-
- if (s->type == SERVICE_DBUS || s->bus_name)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0)
- return r;
-
- if (UNIT(s)->default_dependencies)
- if ((r = service_add_default_dependencies(s)) < 0)
- return r;
- }
-
- return service_verify(s);
-}
-
-static void service_dump(Unit *u, FILE *f, const char *prefix) {
-
- ServiceExecCommand c;
- Service *s = SERVICE(u);
- const char *prefix2;
- char *p2;
-
- assert(s);
-
- p2 = strappend(prefix, "\t");
- prefix2 = p2 ? p2 : prefix;
-
- fprintf(f,
- "%sService State: %s\n"
- "%sResult: %s\n"
- "%sReload Result: %s\n"
- "%sPermissionsStartOnly: %s\n"
- "%sRootDirectoryStartOnly: %s\n"
- "%sRemainAfterExit: %s\n"
- "%sGuessMainPID: %s\n"
- "%sType: %s\n"
- "%sRestart: %s\n"
- "%sNotifyAccess: %s\n",
- prefix, service_state_to_string(s->state),
- prefix, service_result_to_string(s->result),
- prefix, service_result_to_string(s->reload_result),
- prefix, yes_no(s->permissions_start_only),
- prefix, yes_no(s->root_directory_start_only),
- prefix, yes_no(s->remain_after_exit),
- prefix, yes_no(s->guess_main_pid),
- prefix, service_type_to_string(s->type),
- prefix, service_restart_to_string(s->restart),
- prefix, notify_access_to_string(s->notify_access));
-
- if (s->control_pid > 0)
- fprintf(f,
- "%sControl PID: %lu\n",
- prefix, (unsigned long) s->control_pid);
-
- if (s->main_pid > 0)
- fprintf(f,
- "%sMain PID: %lu\n"
- "%sMain PID Known: %s\n"
- "%sMain PID Alien: %s\n",
- prefix, (unsigned long) s->main_pid,
- prefix, yes_no(s->main_pid_known),
- prefix, yes_no(s->main_pid_alien));
-
- if (s->pid_file)
- fprintf(f,
- "%sPIDFile: %s\n",
- prefix, s->pid_file);
-
- if (s->bus_name)
- fprintf(f,
- "%sBusName: %s\n"
- "%sBus Name Good: %s\n",
- prefix, s->bus_name,
- prefix, yes_no(s->bus_name_good));
-
- exec_context_dump(&s->exec_context, f, prefix);
-
- for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
-
- if (!s->exec_command[c])
- continue;
-
- fprintf(f, "%s-> %s:\n",
- prefix, service_exec_command_to_string(c));
-
- exec_command_dump_list(s->exec_command[c], f, prefix2);
- }
-
-#ifdef HAVE_SYSV_COMPAT
- if (s->sysv_path)
- fprintf(f,
- "%sSysV Init Script Path: %s\n"
- "%sSysV Init Script has LSB Header: %s\n"
- "%sSysVEnabled: %s\n",
- prefix, s->sysv_path,
- prefix, yes_no(s->sysv_has_lsb),
- prefix, yes_no(s->sysv_enabled));
-
- if (s->sysv_start_priority >= 0)
- fprintf(f,
- "%sSysVStartPriority: %i\n",
- prefix, s->sysv_start_priority);
-
- if (s->sysv_runlevels)
- fprintf(f, "%sSysVRunLevels: %s\n",
- prefix, s->sysv_runlevels);
-#endif
-
- if (s->fsck_passno > 0)
- fprintf(f,
- "%sFsckPassNo: %i\n",
- prefix, s->fsck_passno);
-
- if (s->status_text)
- fprintf(f, "%sStatus Text: %s\n",
- prefix, s->status_text);
-
- free(p2);
-}
-
-static int service_load_pid_file(Service *s, bool may_warn) {
- char *k;
- int r;
- pid_t pid;
-
- assert(s);
-
- if (!s->pid_file)
- return -ENOENT;
-
- if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
- if (may_warn)
- log_info("PID file %s not readable (yet?) after %s.",
- s->pid_file, service_state_to_string(s->state));
- return r;
- }
-
- r = parse_pid(k, &pid);
- free(k);
-
- if (r < 0)
- return r;
-
- if (kill(pid, 0) < 0 && errno != EPERM) {
- if (may_warn)
- log_info("PID %lu read from file %s does not exist.",
- (unsigned long) pid, s->pid_file);
- return -ESRCH;
- }
-
- if (s->main_pid_known) {
- if (pid == s->main_pid)
- return 0;
-
- log_debug("Main PID changing: %lu -> %lu",
- (unsigned long) s->main_pid, (unsigned long) pid);
- service_unwatch_main_pid(s);
- s->main_pid_known = false;
- } else
- log_debug("Main PID loaded: %lu", (unsigned long) pid);
-
- if ((r = service_set_main_pid(s, pid)) < 0)
- return r;
-
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
- /* FIXME: we need to do something here */
- return r;
-
- return 0;
-}
-
-static int service_search_main_pid(Service *s) {
- pid_t pid;
- int r;
-
- assert(s);
-
- /* If we know it anyway, don't ever fallback to unreliable
- * heuristics */
- if (s->main_pid_known)
- return 0;
-
- if (!s->guess_main_pid)
- return 0;
-
- assert(s->main_pid <= 0);
-
- if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0)
- return -ENOENT;
-
- log_debug("Main PID guessed: %lu", (unsigned long) pid);
- if ((r = service_set_main_pid(s, pid)) < 0)
- return r;
-
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
- /* FIXME: we need to do something here */
- return r;
-
- return 0;
-}
-
-static void service_notify_sockets_dead(Service *s, bool failed_permanent) {
- Iterator i;
- Unit *u;
-
- assert(s);
-
- /* Notifies all our sockets when we die */
-
- if (s->socket_fd >= 0)
- return;
-
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i)
- if (u->type == UNIT_SOCKET)
- socket_notify_service_dead(SOCKET(u), failed_permanent);
-
- return;
-}
-
-static void service_set_state(Service *s, ServiceState state) {
- ServiceState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- service_unwatch_pid_file(s);
-
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL &&
- state != SERVICE_AUTO_RESTART)
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
-
- if (state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RUNNING &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL) {
- service_unwatch_main_pid(s);
- s->main_command = NULL;
- }
-
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL) {
- service_unwatch_control_pid(s);
- s->control_command = NULL;
- s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
- }
-
- if (state == SERVICE_DEAD ||
- state == SERVICE_STOP ||
- state == SERVICE_STOP_SIGTERM ||
- state == SERVICE_STOP_SIGKILL ||
- state == SERVICE_STOP_POST ||
- state == SERVICE_FINAL_SIGTERM ||
- state == SERVICE_FINAL_SIGKILL ||
- state == SERVICE_FAILED ||
- state == SERVICE_AUTO_RESTART)
- service_notify_sockets_dead(s, false);
-
- if (state != SERVICE_START_PRE &&
- state != SERVICE_START &&
- state != SERVICE_START_POST &&
- state != SERVICE_RUNNING &&
- state != SERVICE_RELOAD &&
- state != SERVICE_STOP &&
- state != SERVICE_STOP_SIGTERM &&
- state != SERVICE_STOP_SIGKILL &&
- state != SERVICE_STOP_POST &&
- state != SERVICE_FINAL_SIGTERM &&
- state != SERVICE_FINAL_SIGKILL &&
- !(state == SERVICE_DEAD && UNIT(s)->job)) {
- service_close_socket_fd(s);
- service_connection_unref(s);
- }
-
- if (state == SERVICE_STOP)
- service_stop_watchdog(s);
-
- /* For the inactive states unit_notify() will trim the cgroup,
- * but for exit we have to do that ourselves... */
- if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
- cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
-
- if (old_state != state)
- log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], s->reload_result == SERVICE_SUCCESS);
- s->reload_result = SERVICE_SUCCESS;
-}
-
-static int service_coldplug(Unit *u) {
- Service *s = SERVICE(u);
- int r;
-
- assert(s);
- assert(s->state == SERVICE_DEAD);
-
- if (s->deserialized_state != s->state) {
-
- if (s->deserialized_state == SERVICE_START_PRE ||
- s->deserialized_state == SERVICE_START ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL ||
- s->deserialized_state == SERVICE_STOP_POST ||
- s->deserialized_state == SERVICE_FINAL_SIGTERM ||
- s->deserialized_state == SERVICE_FINAL_SIGKILL ||
- s->deserialized_state == SERVICE_AUTO_RESTART) {
-
- if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) {
- usec_t k;
-
- k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec;
-
- if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0)
- return r;
- }
- }
-
- if ((s->deserialized_state == SERVICE_START &&
- (s->type == SERVICE_FORKING ||
- s->type == SERVICE_DBUS ||
- s->type == SERVICE_ONESHOT ||
- s->type == SERVICE_NOTIFY)) ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RUNNING ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL)
- if (s->main_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
- return r;
-
- if (s->deserialized_state == SERVICE_START_PRE ||
- s->deserialized_state == SERVICE_START ||
- s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RELOAD ||
- s->deserialized_state == SERVICE_STOP ||
- s->deserialized_state == SERVICE_STOP_SIGTERM ||
- s->deserialized_state == SERVICE_STOP_SIGKILL ||
- s->deserialized_state == SERVICE_STOP_POST ||
- s->deserialized_state == SERVICE_FINAL_SIGTERM ||
- s->deserialized_state == SERVICE_FINAL_SIGKILL)
- if (s->control_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
- return r;
-
- if (s->deserialized_state == SERVICE_START_POST ||
- s->deserialized_state == SERVICE_RUNNING)
- service_handle_watchdog(s);
-
- service_set_state(s, s->deserialized_state);
- }
- return 0;
-}
-
-static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
- Iterator i;
- int r;
- int *rfds = NULL;
- unsigned rn_fds = 0;
- Unit *u;
-
- assert(s);
- assert(fds);
- assert(n_fds);
-
- if (s->socket_fd >= 0)
- return 0;
-
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
- int *cfds;
- unsigned cn_fds;
- Socket *sock;
-
- if (u->type != UNIT_SOCKET)
- continue;
-
- sock = SOCKET(u);
-
- if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0)
- goto fail;
-
- if (!cfds)
- continue;
-
- if (!rfds) {
- rfds = cfds;
- rn_fds = cn_fds;
- } else {
- int *t;
-
- if (!(t = new(int, rn_fds+cn_fds))) {
- free(cfds);
- r = -ENOMEM;
- goto fail;
- }
-
- memcpy(t, rfds, rn_fds * sizeof(int));
- memcpy(t+rn_fds, cfds, cn_fds * sizeof(int));
- free(rfds);
- free(cfds);
-
- rfds = t;
- rn_fds = rn_fds+cn_fds;
- }
- }
-
- *fds = rfds;
- *n_fds = rn_fds;
-
- return 0;
-
-fail:
- free(rfds);
-
- return r;
-}
-
-static int service_spawn(
- Service *s,
- ExecCommand *c,
- bool timeout,
- bool pass_fds,
- bool apply_permissions,
- bool apply_chroot,
- bool apply_tty_stdin,
- bool set_notify_socket,
- pid_t *_pid) {
-
- pid_t pid;
- int r;
- int *fds = NULL, *fdsbuf = NULL;
- unsigned n_fds = 0, n_env = 0;
- char **argv = NULL, **final_env = NULL, **our_env = NULL;
-
- assert(s);
- assert(c);
- assert(_pid);
-
- if (pass_fds ||
- s->exec_context.std_input == EXEC_INPUT_SOCKET ||
- s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
- s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
-
- if (s->socket_fd >= 0) {
- fds = &s->socket_fd;
- n_fds = 1;
- } else {
- if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0)
- goto fail;
-
- fds = fdsbuf;
- }
- }
-
- if (timeout && s->timeout_usec) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
- } else
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
-
- if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!(our_env = new0(char*, 4))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (set_notify_socket)
- if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (s->main_pid > 0)
- if (asprintf(our_env + n_env++, "MAINPID=%lu", (unsigned long) s->main_pid) < 0) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (s->watchdog_usec > 0)
- if (asprintf(our_env + n_env++, "WATCHDOG_USEC=%llu", (unsigned long long) s->watchdog_usec) < 0) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!(final_env = strv_env_merge(2,
- UNIT(s)->manager->environment,
- our_env,
- NULL))) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = exec_spawn(c,
- argv,
- &s->exec_context,
- fds, n_fds,
- final_env,
- apply_permissions,
- apply_chroot,
- apply_tty_stdin,
- UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- &pid);
-
- if (r < 0)
- goto fail;
-
-
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
- /* FIXME: we need to do something here */
- goto fail;
-
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
- *_pid = pid;
-
- return 0;
-
-fail:
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
- if (timeout)
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
-
- return r;
-}
-
-static int main_pid_good(Service *s) {
- assert(s);
-
- /* Returns 0 if the pid is dead, 1 if it is good, -1 if we
- * don't know */
-
- /* If we know the pid file, then lets just check if it is
- * still valid */
- if (s->main_pid_known) {
-
- /* If it's an alien child let's check if it is still
- * alive ... */
- if (s->main_pid_alien)
- return kill(s->main_pid, 0) >= 0 || errno != ESRCH;
-
- /* .. otherwise assume we'll get a SIGCHLD for it,
- * which we really should wait for to collect exit
- * status and code */
- return s->main_pid > 0;
- }
-
- /* We don't know the pid */
- return -EAGAIN;
-}
-
-static int control_pid_good(Service *s) {
- assert(s);
-
- return s->control_pid > 0;
-}
-
-static int cgroup_good(Service *s) {
- int r;
-
- assert(s);
-
- if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0)
- return r;
-
- return !r;
-}
-
-static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
- int r;
- assert(s);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- if (allow_restart &&
- !s->forbid_restart &&
- (s->restart == SERVICE_RESTART_ALWAYS ||
- (s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
- (s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
- (s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
- s->result == SERVICE_FAILURE_CORE_DUMP)))) {
-
- r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
- if (r < 0)
- goto fail;
-
- service_set_state(s, SERVICE_AUTO_RESTART);
- } else
- service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
-
- s->forbid_restart = false;
-
- return;
-
-fail:
- log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r));
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
-}
-
-static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
-
-static void service_enter_stop_post(Service *s, ServiceResult f) {
- int r;
- assert(s);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- service_unwatch_control_pid(s);
-
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
- s->control_command_id = SERVICE_EXEC_STOP_POST;
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
-
- service_set_state(s, SERVICE_STOP_POST);
- } else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
- int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
-
- assert(s);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- if (s->exec_context.kill_mode != KILL_NONE) {
- int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
-
- if (s->main_pid > 0) {
- if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill main process %li: %m", (long) s->main_pid);
- else
- wait_for_exit = !s->main_pid_alien;
- }
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the main/control pids from being killed via the cgroup */
- if (s->main_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0)
- goto fail;
-
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning("Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
-
- if (wait_for_exit) {
- if (s->timeout_usec > 0)
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
-
- service_set_state(s, state);
- } else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
- service_enter_stop_post(s, SERVICE_SUCCESS);
- else
- service_enter_dead(s, SERVICE_SUCCESS, true);
-
- return;
-
-fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
-
- if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
- service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
- else
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
-
- if (pid_set)
- set_free(pid_set);
-}
-
-static void service_enter_stop(Service *s, ServiceResult f) {
- int r;
-
- assert(s);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- service_unwatch_control_pid(s);
-
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
- s->control_command_id = SERVICE_EXEC_STOP;
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
- service_set_state(s, SERVICE_STOP);
- } else
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r));
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_enter_running(Service *s, ServiceResult f) {
- int main_pid_ok, cgroup_ok;
- assert(s);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- main_pid_ok = main_pid_good(s);
- cgroup_ok = cgroup_good(s);
-
- if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
- (s->bus_name_good || s->type != SERVICE_DBUS))
- service_set_state(s, SERVICE_RUNNING);
- else if (s->remain_after_exit)
- service_set_state(s, SERVICE_EXITED);
- else
- service_enter_stop(s, SERVICE_SUCCESS);
-}
-
-static void service_enter_start_post(Service *s) {
- int r;
- assert(s);
-
- service_unwatch_control_pid(s);
-
- if (s->watchdog_usec > 0)
- service_reset_watchdog(s);
-
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
- s->control_command_id = SERVICE_EXEC_START_POST;
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
- service_set_state(s, SERVICE_START_POST);
- } else
- service_enter_running(s, SERVICE_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
- service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_enter_start(Service *s) {
- pid_t pid;
- int r;
- ExecCommand *c;
-
- assert(s);
-
- assert(s->exec_command[SERVICE_EXEC_START]);
- assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT);
-
- if (s->type == SERVICE_FORKING)
- service_unwatch_control_pid(s);
- else
- service_unwatch_main_pid(s);
-
- /* We want to ensure that nobody leaks processes from
- * START_PRE here, so let's go on a killing spree, People
- * should not spawn long running processes from START_PRE. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL);
-
- if (s->type == SERVICE_FORKING) {
- s->control_command_id = SERVICE_EXEC_START;
- c = s->control_command = s->exec_command[SERVICE_EXEC_START];
-
- s->main_command = NULL;
- } else {
- s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
- s->control_command = NULL;
-
- c = s->main_command = s->exec_command[SERVICE_EXEC_START];
- }
-
- if ((r = service_spawn(s,
- c,
- s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY,
- true,
- true,
- true,
- true,
- s->notify_access != NOTIFY_NONE,
- &pid)) < 0)
- goto fail;
-
- if (s->type == SERVICE_SIMPLE) {
- /* For simple services we immediately start
- * the START_POST binaries. */
-
- service_set_main_pid(s, pid);
- service_enter_start_post(s);
-
- } else if (s->type == SERVICE_FORKING) {
-
- /* For forking services we wait until the start
- * process exited. */
-
- s->control_pid = pid;
- service_set_state(s, SERVICE_START);
-
- } else if (s->type == SERVICE_ONESHOT ||
- s->type == SERVICE_DBUS ||
- s->type == SERVICE_NOTIFY) {
-
- /* For oneshot services we wait until the start
- * process exited, too, but it is our main process. */
-
- /* For D-Bus services we know the main pid right away,
- * but wait for the bus name to appear on the
- * bus. Notify services are similar. */
-
- service_set_main_pid(s, pid);
- service_set_state(s, SERVICE_START);
- } else
- assert_not_reached("Unknown service type");
-
- return;
-
-fail:
- log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r));
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_enter_start_pre(Service *s) {
- int r;
-
- assert(s);
-
- service_unwatch_control_pid(s);
-
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
-
- /* Before we start anything, let's clear up what might
- * be left from previous runs. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, NULL);
-
- s->control_command_id = SERVICE_EXEC_START_PRE;
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
- service_set_state(s, SERVICE_START_PRE);
- } else
- service_enter_start(s);
-
- return;
-
-fail:
- log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
-}
-
-static void service_enter_restart(Service *s) {
- int r;
- DBusError error;
-
- assert(s);
- dbus_error_init(&error);
-
- if (UNIT(s)->job) {
- log_info("Job pending for unit, delaying automatic restart.");
-
- if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0)
- goto fail;
- }
-
- /* Any units that are bound to this service must also be
- * restarted. We use JOB_RESTART (instead of the more obvious
- * JOB_START) here so that those dependency jobs will be added
- * as well. */
- r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL);
- if (r < 0)
- goto fail;
-
- log_debug("%s scheduled restart job.", UNIT(s)->id);
- return;
-
-fail:
- log_warning("%s failed to schedule restart job: %s", UNIT(s)->id, bus_error(&error, -r));
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
-
- dbus_error_free(&error);
-}
-
-static void service_enter_reload(Service *s) {
- int r;
-
- assert(s);
-
- service_unwatch_control_pid(s);
-
- if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
- s->control_command_id = SERVICE_EXEC_RELOAD;
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
- service_set_state(s, SERVICE_RELOAD);
- } else
- service_enter_running(s, SERVICE_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r));
- s->reload_result = SERVICE_FAILURE_RESOURCES;
- service_enter_running(s, SERVICE_SUCCESS);
-}
-
-static void service_run_next_control(Service *s) {
- int r;
-
- assert(s);
- assert(s->control_command);
- assert(s->control_command->command_next);
-
- assert(s->control_command_id != SERVICE_EXEC_START);
-
- s->control_command = s->control_command->command_next;
- service_unwatch_control_pid(s);
-
- if ((r = service_spawn(s,
- s->control_command,
- true,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- s->control_command_id == SERVICE_EXEC_START_PRE ||
- s->control_command_id == SERVICE_EXEC_STOP_POST,
- false,
- &s->control_pid)) < 0)
- goto fail;
-
- return;
-
-fail:
- log_warning("%s failed to run next control task: %s", UNIT(s)->id, strerror(-r));
-
- if (s->state == SERVICE_START_PRE)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
- else if (s->state == SERVICE_STOP)
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
- else if (s->state == SERVICE_STOP_POST)
- service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
- else if (s->state == SERVICE_RELOAD) {
- s->reload_result = SERVICE_FAILURE_RESOURCES;
- service_enter_running(s, SERVICE_SUCCESS);
- } else
- service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_run_next_main(Service *s) {
- pid_t pid;
- int r;
-
- assert(s);
- assert(s->main_command);
- assert(s->main_command->command_next);
- assert(s->type == SERVICE_ONESHOT);
-
- s->main_command = s->main_command->command_next;
- service_unwatch_main_pid(s);
-
- if ((r = service_spawn(s,
- s->main_command,
- false,
- true,
- true,
- true,
- true,
- s->notify_access != NOTIFY_NONE,
- &pid)) < 0)
- goto fail;
-
- service_set_main_pid(s, pid);
-
- return;
-
-fail:
- log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r));
- service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
-}
-
-static int service_start_limit_test(Service *s) {
- assert(s);
-
- if (ratelimit_test(&s->start_limit))
- return 0;
-
- switch (s->start_limit_action) {
-
- case SERVICE_START_LIMIT_NONE:
- log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id);
- break;
-
- case SERVICE_START_LIMIT_REBOOT: {
- DBusError error;
- int r;
-
- dbus_error_init(&error);
-
- log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id);
-
- r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
- if (r < 0) {
- log_error("Failed to reboot: %s.", bus_error(&error, r));
- dbus_error_free(&error);
- }
-
- break;
- }
-
- case SERVICE_START_LIMIT_REBOOT_FORCE:
- log_warning("%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id);
- UNIT(s)->manager->exit_code = MANAGER_REBOOT;
- break;
-
- case SERVICE_START_LIMIT_REBOOT_IMMEDIATE:
- log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id);
- reboot(RB_AUTOBOOT);
- break;
-
- default:
- log_error("start limit action=%i", s->start_limit_action);
- assert_not_reached("Unknown StartLimitAction.");
- }
-
- return -ECANCELED;
-}
-
-static int service_start(Unit *u) {
- Service *s = SERVICE(u);
- int r;
-
- assert(s);
-
- /* We cannot fulfill this request right now, try again later
- * please! */
- if (s->state == SERVICE_STOP ||
- s->state == SERVICE_STOP_SIGTERM ||
- s->state == SERVICE_STOP_SIGKILL ||
- s->state == SERVICE_STOP_POST ||
- s->state == SERVICE_FINAL_SIGTERM ||
- s->state == SERVICE_FINAL_SIGKILL)
- return -EAGAIN;
-
- /* Already on it! */
- if (s->state == SERVICE_START_PRE ||
- s->state == SERVICE_START ||
- s->state == SERVICE_START_POST)
- return 0;
-
- assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART);
-
- /* Make sure we don't enter a busy loop of some kind. */
- r = service_start_limit_test(s);
- if (r < 0) {
- service_notify_sockets_dead(s, true);
- return r;
- }
-
- s->result = SERVICE_SUCCESS;
- s->reload_result = SERVICE_SUCCESS;
- s->main_pid_known = false;
- s->main_pid_alien = false;
- s->forbid_restart = false;
-
- service_enter_start_pre(s);
- return 0;
-}
-
-static int service_stop(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- /* This is a user request, so don't do restarts on this
- * shutdown. */
- s->forbid_restart = true;
-
- /* Already on it */
- if (s->state == SERVICE_STOP ||
- s->state == SERVICE_STOP_SIGTERM ||
- s->state == SERVICE_STOP_SIGKILL ||
- s->state == SERVICE_STOP_POST ||
- s->state == SERVICE_FINAL_SIGTERM ||
- s->state == SERVICE_FINAL_SIGKILL)
- return 0;
-
- /* Don't allow a restart */
- if (s->state == SERVICE_AUTO_RESTART) {
- service_set_state(s, SERVICE_DEAD);
- return 0;
- }
-
- /* If there's already something running we go directly into
- * kill mode. */
- if (s->state == SERVICE_START_PRE ||
- s->state == SERVICE_START ||
- s->state == SERVICE_START_POST ||
- s->state == SERVICE_RELOAD) {
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
- return 0;
- }
-
- assert(s->state == SERVICE_RUNNING ||
- s->state == SERVICE_EXITED);
-
- service_enter_stop(s, SERVICE_SUCCESS);
- return 0;
-}
-
-static int service_reload(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED);
-
- service_enter_reload(s);
- return 0;
-}
-
-static bool service_can_reload(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- return !!s->exec_command[SERVICE_EXEC_RELOAD];
-}
-
-static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
- Service *s = SERVICE(u);
-
- assert(u);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", service_state_to_string(s->state));
- unit_serialize_item(u, f, "result", service_result_to_string(s->result));
- unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
-
- if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
-
- if (s->main_pid_known && s->main_pid > 0)
- unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid);
-
- unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
-
- if (s->status_text)
- unit_serialize_item(u, f, "status-text", s->status_text);
-
- /* FIXME: There's a minor uncleanliness here: if there are
- * multiple commands attached here, we will start from the
- * first one again */
- if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
-
- if (s->socket_fd >= 0) {
- int copy;
-
- if ((copy = fdset_put_dup(fds, s->socket_fd)) < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
- }
-
- if (s->main_exec_status.pid > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid);
- dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
- dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
-
- if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
- unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
- unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
- }
- }
- if (dual_timestamp_is_set(&s->watchdog_timestamp))
- dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
-
- return 0;
-}
-
-static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Service *s = SERVICE(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- ServiceState state;
-
- if ((state = service_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- s->deserialized_state = state;
- } else if (streq(key, "result")) {
- ServiceResult f;
-
- f = service_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != SERVICE_SUCCESS)
- s->result = f;
-
- } else if (streq(key, "reload-result")) {
- ServiceResult f;
-
- f = service_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse reload result value %s", value);
- else if (f != SERVICE_SUCCESS)
- s->reload_result = f;
-
- } else if (streq(key, "control-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
- else
- s->control_pid = pid;
- } else if (streq(key, "main-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse main-pid value %s", value);
- else
- service_set_main_pid(s, (pid_t) pid);
- } else if (streq(key, "main-pid-known")) {
- int b;
-
- if ((b = parse_boolean(value)) < 0)
- log_debug("Failed to parse main-pid-known value %s", value);
- else
- s->main_pid_known = b;
- } else if (streq(key, "status-text")) {
- char *t;
-
- if ((t = strdup(value))) {
- free(s->status_text);
- s->status_text = t;
- }
-
- } else if (streq(key, "control-command")) {
- ServiceExecCommand id;
-
- if ((id = service_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
- else {
- s->control_command_id = id;
- s->control_command = s->exec_command[id];
- }
- } else if (streq(key, "socket-fd")) {
- int fd;
-
- if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket-fd value %s", value);
- else {
-
- if (s->socket_fd >= 0)
- close_nointr_nofail(s->socket_fd);
- s->socket_fd = fdset_remove(fds, fd);
- }
- } else if (streq(key, "main-exec-status-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse main-exec-status-pid value %s", value);
- else
- s->main_exec_status.pid = pid;
- } else if (streq(key, "main-exec-status-code")) {
- int i;
-
- if (safe_atoi(value, &i) < 0)
- log_debug("Failed to parse main-exec-status-code value %s", value);
- else
- s->main_exec_status.code = i;
- } else if (streq(key, "main-exec-status-status")) {
- int i;
-
- if (safe_atoi(value, &i) < 0)
- log_debug("Failed to parse main-exec-status-status value %s", value);
- else
- s->main_exec_status.status = i;
- } else if (streq(key, "main-exec-status-start"))
- dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
- else if (streq(key, "main-exec-status-exit"))
- dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
- else if (streq(key, "watchdog-timestamp"))
- dual_timestamp_deserialize(value, &s->watchdog_timestamp);
- else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState service_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SERVICE(u)->state];
-}
-
-static const char *service_sub_state_to_string(Unit *u) {
- assert(u);
-
- return service_state_to_string(SERVICE(u)->state);
-}
-
-static bool service_check_gc(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- /* Never clean up services that still have a process around,
- * even if the service is formally dead. */
- if (cgroup_good(s) > 0 ||
- main_pid_good(s) > 0 ||
- control_pid_good(s) > 0)
- return true;
-
-#ifdef HAVE_SYSV_COMPAT
- if (s->sysv_path)
- return true;
-#endif
-
- return false;
-}
-
-static bool service_check_snapshot(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- return !s->got_socket_fd;
-}
-
-static int service_retry_pid_file(Service *s) {
- int r;
-
- assert(s->pid_file);
- assert(s->state == SERVICE_START || s->state == SERVICE_START_POST);
-
- r = service_load_pid_file(s, false);
- if (r < 0)
- return r;
-
- service_unwatch_pid_file(s);
-
- service_enter_running(s, SERVICE_SUCCESS);
- return 0;
-}
-
-static int service_watch_pid_file(Service *s) {
- int r;
-
- log_debug("Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
- r = path_spec_watch(s->pid_file_pathspec, UNIT(s));
- if (r < 0)
- goto fail;
-
- /* the pidfile might have appeared just before we set the watch */
- service_retry_pid_file(s);
-
- return 0;
-fail:
- log_error("Failed to set a watch for %s's PID file %s: %s",
- UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r));
- service_unwatch_pid_file(s);
- return r;
-}
-
-static int service_demand_pid_file(Service *s) {
- PathSpec *ps;
-
- assert(s->pid_file);
- assert(!s->pid_file_pathspec);
-
- ps = new0(PathSpec, 1);
- if (!ps)
- return -ENOMEM;
-
- ps->path = strdup(s->pid_file);
- if (!ps->path) {
- free(ps);
- return -ENOMEM;
- }
-
- path_kill_slashes(ps->path);
-
- /* PATH_CHANGED would not be enough. There are daemons (sendmail) that
- * keep their PID file open all the time. */
- ps->type = PATH_MODIFIED;
- ps->inotify_fd = -1;
-
- s->pid_file_pathspec = ps;
-
- return service_watch_pid_file(s);
-}
-
-static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
- Service *s = SERVICE(u);
-
- assert(s);
- assert(fd >= 0);
- assert(s->state == SERVICE_START || s->state == SERVICE_START_POST);
- assert(s->pid_file_pathspec);
- assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd));
-
- log_debug("inotify event for %s", u->id);
-
- if (path_spec_fd_event(s->pid_file_pathspec, events) < 0)
- goto fail;
-
- if (service_retry_pid_file(s) == 0)
- return;
-
- if (service_watch_pid_file(s) < 0)
- goto fail;
-
- return;
-fail:
- service_unwatch_pid_file(s);
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
-}
-
-static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- Service *s = SERVICE(u);
- ServiceResult f;
-
- assert(s);
- assert(pid >= 0);
-
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
- f = SERVICE_SUCCESS;
- else if (code == CLD_EXITED)
- f = SERVICE_FAILURE_EXIT_CODE;
- else if (code == CLD_KILLED)
- f = SERVICE_FAILURE_SIGNAL;
- else if (code == CLD_DUMPED)
- f = SERVICE_FAILURE_CORE_DUMP;
- else
- assert_not_reached("Unknown code");
-
- if (s->main_pid == pid) {
- /* Forking services may occasionally move to a new PID.
- * As long as they update the PID file before exiting the old
- * PID, they're fine. */
- if (service_load_pid_file(s, false) == 0)
- return;
-
- s->main_pid = 0;
- exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
-
- /* If this is not a forking service than the main
- * process got started and hence we copy the exit
- * status so that it is recorded both as main and as
- * control process exit status */
- if (s->main_command) {
- s->main_command->exec_status = s->main_exec_status;
-
- if (s->main_command->ignore)
- f = SERVICE_SUCCESS;
- }
-
- log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- if (s->main_command &&
- s->main_command->command_next &&
- f == SERVICE_SUCCESS) {
-
- /* There is another command to *
- * execute, so let's do that. */
-
- log_debug("%s running next main command for state %s", u->id, service_state_to_string(s->state));
- service_run_next_main(s);
-
- } else {
-
- /* The service exited, so the service is officially
- * gone. */
- s->main_command = NULL;
-
- switch (s->state) {
-
- case SERVICE_START_POST:
- case SERVICE_RELOAD:
- case SERVICE_STOP:
- /* Need to wait until the operation is
- * done */
- break;
-
- case SERVICE_START:
- if (s->type == SERVICE_ONESHOT) {
- /* This was our main goal, so let's go on */
- if (f == SERVICE_SUCCESS)
- service_enter_start_post(s);
- else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
- break;
- } else {
- assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
-
- /* Fall through */
- }
-
- case SERVICE_RUNNING:
- service_enter_running(s, f);
- break;
-
- case SERVICE_STOP_SIGTERM:
- case SERVICE_STOP_SIGKILL:
-
- if (!control_pid_good(s))
- service_enter_stop_post(s, f);
-
- /* If there is still a control process, wait for that first */
- break;
-
- default:
- assert_not_reached("Uh, main process died at wrong time.");
- }
- }
-
- } else if (s->control_pid == pid) {
-
- s->control_pid = 0;
-
- if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
-
- if (s->control_command->ignore)
- f = SERVICE_SUCCESS;
- }
-
- log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
-
- if (f != SERVICE_SUCCESS)
- s->result = f;
-
- if (s->control_command &&
- s->control_command->command_next &&
- f == SERVICE_SUCCESS) {
-
- /* There is another command to *
- * execute, so let's do that. */
-
- log_debug("%s running next control command for state %s", u->id, service_state_to_string(s->state));
- service_run_next_control(s);
-
- } else {
- /* No further commands for this step, so let's
- * figure out what to do next */
-
- s->control_command = NULL;
- s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
-
- log_debug("%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state));
-
- switch (s->state) {
-
- case SERVICE_START_PRE:
- if (f == SERVICE_SUCCESS)
- service_enter_start(s);
- else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
- break;
-
- case SERVICE_START:
- assert(s->type == SERVICE_FORKING);
-
- if (f != SERVICE_SUCCESS) {
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
- break;
- }
-
- if (s->pid_file) {
- bool has_start_post;
- int r;
-
- /* Let's try to load the pid file here if we can.
- * The PID file might actually be created by a START_POST
- * script. In that case don't worry if the loading fails. */
-
- has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST];
- r = service_load_pid_file(s, !has_start_post);
- if (!has_start_post && r < 0) {
- r = service_demand_pid_file(s);
- if (r < 0 || !cgroup_good(s))
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
- break;
- }
- } else
- service_search_main_pid(s);
-
- service_enter_start_post(s);
- break;
-
- case SERVICE_START_POST:
- if (f != SERVICE_SUCCESS) {
- service_enter_stop(s, f);
- break;
- }
-
- if (s->pid_file) {
- int r;
-
- r = service_load_pid_file(s, true);
- if (r < 0) {
- r = service_demand_pid_file(s);
- if (r < 0 || !cgroup_good(s))
- service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
- break;
- }
- } else
- service_search_main_pid(s);
-
- service_enter_running(s, SERVICE_SUCCESS);
- break;
-
- case SERVICE_RELOAD:
- if (f == SERVICE_SUCCESS) {
- service_load_pid_file(s, true);
- service_search_main_pid(s);
- }
-
- s->reload_result = f;
- service_enter_running(s, SERVICE_SUCCESS);
- break;
-
- case SERVICE_STOP:
- service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
- break;
-
- case SERVICE_STOP_SIGTERM:
- case SERVICE_STOP_SIGKILL:
- if (main_pid_good(s) <= 0)
- service_enter_stop_post(s, f);
-
- /* If there is still a service
- * process around, wait until
- * that one quit, too */
- break;
-
- case SERVICE_STOP_POST:
- case SERVICE_FINAL_SIGTERM:
- case SERVICE_FINAL_SIGKILL:
- service_enter_dead(s, f, true);
- break;
-
- default:
- assert_not_reached("Uh, control process died at wrong time.");
- }
- }
- }
-
- /* Notify clients about changed exit status */
- unit_add_to_dbus_queue(u);
-}
-
-static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
- Service *s = SERVICE(u);
-
- assert(s);
- assert(elapsed == 1);
-
- if (w == &s->watchdog_watch) {
- service_handle_watchdog(s);
- return;
- }
-
- assert(w == &s->timer_watch);
-
- switch (s->state) {
-
- case SERVICE_START_PRE:
- case SERVICE_START:
- log_warning("%s operation timed out. Terminating.", u->id);
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
- break;
-
- case SERVICE_START_POST:
- log_warning("%s operation timed out. Stopping.", u->id);
- service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
- break;
-
- case SERVICE_RELOAD:
- log_warning("%s operation timed out. Stopping.", u->id);
- s->reload_result = SERVICE_FAILURE_TIMEOUT;
- service_enter_running(s, SERVICE_SUCCESS);
- break;
-
- case SERVICE_STOP:
- log_warning("%s stopping timed out. Terminating.", u->id);
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
- break;
-
- case SERVICE_STOP_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s stopping timed out. Killing.", u->id);
- service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
- } else {
- log_warning("%s stopping timed out. Skipping SIGKILL.", u->id);
- service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
- }
-
- break;
-
- case SERVICE_STOP_SIGKILL:
- /* Uh, we sent a SIGKILL and it is still not gone?
- * Must be something we cannot kill, so let's just be
- * weirded out and continue */
-
- log_warning("%s still around after SIGKILL. Ignoring.", u->id);
- service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
- break;
-
- case SERVICE_STOP_POST:
- log_warning("%s stopping timed out (2). Terminating.", u->id);
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
- break;
-
- case SERVICE_FINAL_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s stopping timed out (2). Killing.", u->id);
- service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
- } else {
- log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->id);
- service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
- }
-
- break;
-
- case SERVICE_FINAL_SIGKILL:
- log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
- service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true);
- break;
-
- case SERVICE_AUTO_RESTART:
- log_info("%s holdoff time over, scheduling restart.", u->id);
- service_enter_restart(s);
- break;
-
- default:
- assert_not_reached("Timeout at wrong time.");
- }
-}
-
-static void service_cgroup_notify_event(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(u);
-
- log_debug("%s: cgroup is empty", u->id);
-
- switch (s->state) {
-
- /* Waiting for SIGCHLD is usually more interesting,
- * because it includes return codes/signals. Which is
- * why we ignore the cgroup events for most cases,
- * except when we don't know pid which to expect the
- * SIGCHLD for. */
-
- case SERVICE_START:
- case SERVICE_START_POST:
- /* If we were hoping for the daemon to write its PID file,
- * we can give up now. */
- if (s->pid_file_pathspec) {
- log_warning("%s never wrote its PID file. Failing.", UNIT(s)->id);
- service_unwatch_pid_file(s);
- if (s->state == SERVICE_START)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
- else
- service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
- }
- break;
-
- case SERVICE_RUNNING:
- /* service_enter_running() will figure out what to do */
- service_enter_running(s, SERVICE_SUCCESS);
- break;
-
- case SERVICE_STOP_SIGTERM:
- case SERVICE_STOP_SIGKILL:
-
- if (main_pid_good(s) <= 0 && !control_pid_good(s))
- service_enter_stop_post(s, SERVICE_SUCCESS);
-
- break;
-
- case SERVICE_FINAL_SIGTERM:
- case SERVICE_FINAL_SIGKILL:
- if (main_pid_good(s) <= 0 && !control_pid_good(s))
- service_enter_dead(s, SERVICE_SUCCESS, SERVICE_SUCCESS);
-
- break;
-
- default:
- ;
- }
-}
-
-static void service_notify_message(Unit *u, pid_t pid, char **tags) {
- Service *s = SERVICE(u);
- const char *e;
-
- assert(u);
-
- if (s->notify_access == NOTIFY_NONE) {
- log_warning("%s: Got notification message from PID %lu, but reception is disabled.",
- u->id, (unsigned long) pid);
- return;
- }
-
- if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
- log_warning("%s: Got notification message from PID %lu, but reception only permitted for PID %lu",
- u->id, (unsigned long) pid, (unsigned long) s->main_pid);
- return;
- }
-
- log_debug("%s: Got message", u->id);
-
- /* Interpret MAINPID= */
- if ((e = strv_find_prefix(tags, "MAINPID=")) &&
- (s->state == SERVICE_START ||
- s->state == SERVICE_START_POST ||
- s->state == SERVICE_RUNNING ||
- s->state == SERVICE_RELOAD)) {
-
- if (parse_pid(e + 8, &pid) < 0)
- log_warning("Failed to parse notification message %s", e);
- else {
- log_debug("%s: got %s", u->id, e);
- service_set_main_pid(s, pid);
- }
- }
-
- /* Interpret READY= */
- if (s->type == SERVICE_NOTIFY &&
- s->state == SERVICE_START &&
- strv_find(tags, "READY=1")) {
- log_debug("%s: got READY=1", u->id);
-
- service_enter_start_post(s);
- }
-
- /* Interpret STATUS= */
- e = strv_find_prefix(tags, "STATUS=");
- if (e) {
- char *t;
-
- if (e[7]) {
-
- if (!utf8_is_valid(e+7)) {
- log_warning("Status message in notification is not UTF-8 clean.");
- return;
- }
-
- t = strdup(e+7);
- if (!t) {
- log_error("Failed to allocate string.");
- return;
- }
-
- log_debug("%s: got %s", u->id, e);
-
- free(s->status_text);
- s->status_text = t;
- } else {
- free(s->status_text);
- s->status_text = NULL;
- }
-
- }
- if (strv_find(tags, "WATCHDOG=1")) {
- log_debug("%s: got WATCHDOG=1", u->id);
- service_reset_watchdog(s);
- }
-
- /* Notify clients about changed status or main pid */
- unit_add_to_dbus_queue(u);
-}
-
-#ifdef HAVE_SYSV_COMPAT
-
-#ifdef TARGET_SUSE
-static void sysv_facility_in_insserv_conf(Manager *mgr) {
- FILE *f=NULL;
- int r;
-
- if (!(f = fopen("/etc/insserv.conf", "re"))) {
- r = errno == ENOENT ? 0 : -errno;
- goto finish;
- }
-
- while (!feof(f)) {
- char l[LINE_MAX], *t;
- char **parsed = NULL;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r));
- goto finish;
- }
-
- t = strstrip(l);
- if (*t != '$' && *t != '<')
- continue;
-
- parsed = strv_split(t,WHITESPACE);
- /* we ignore <interactive>, not used, equivalent to X-Interactive */
- if (parsed && !startswith_no_case (parsed[0], "<interactive>")) {
- char *facility;
- Unit *u;
- if (sysv_translate_facility(parsed[0], NULL, &facility) < 0)
- continue;
- if ((u = manager_get_unit(mgr, facility)) && (u->type == UNIT_TARGET)) {
- UnitDependency e;
- char *dep = NULL, *name, **j;
-
- STRV_FOREACH (j, parsed+1) {
- if (*j[0]=='+') {
- e = UNIT_WANTS;
- name = *j+1;
- }
- else {
- e = UNIT_REQUIRES;
- name = *j;
- }
- if (sysv_translate_facility(name, NULL, &dep) < 0)
- continue;
-
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true);
- free(dep);
- }
- }
- free(facility);
- }
- strv_free(parsed);
- }
-finish:
- if (f)
- fclose(f);
-
-}
-#endif
-
-static int service_enumerate(Manager *m) {
- char **p;
- unsigned i;
- DIR *d = NULL;
- char *path = NULL, *fpath = NULL, *name = NULL;
- Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL;
- Unit *service;
- Iterator j;
- int r;
-
- assert(m);
-
- if (m->running_as != MANAGER_SYSTEM)
- return 0;
-
- zero(runlevel_services);
-
- STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
- for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
- struct dirent *de;
-
- free(path);
- path = join(*p, "/", rcnd_table[i].path, NULL);
- if (!path) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (d)
- closedir(d);
-
- if (!(d = opendir(path))) {
- if (errno != ENOENT)
- log_warning("opendir() failed on %s: %s", path, strerror(errno));
-
- continue;
- }
-
- while ((de = readdir(d))) {
- int a, b;
-
- if (ignore_file(de->d_name))
- continue;
-
- if (de->d_name[0] != 'S' && de->d_name[0] != 'K')
- continue;
-
- if (strlen(de->d_name) < 4)
- continue;
-
- a = undecchar(de->d_name[1]);
- b = undecchar(de->d_name[2]);
-
- if (a < 0 || b < 0)
- continue;
-
- free(fpath);
- fpath = join(path, "/", de->d_name, NULL);
- if (!fpath) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (access(fpath, X_OK) < 0) {
-
- if (errno != ENOENT)
- log_warning("access() failed on %s: %s", fpath, strerror(errno));
-
- continue;
- }
-
- free(name);
- if (!(name = sysv_translate_name(de->d_name + 3))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 0) {
- log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
- continue;
- }
-
- if (de->d_name[0] == 'S') {
-
- if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) {
- SERVICE(service)->sysv_start_priority_from_rcnd =
- MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd);
-
- SERVICE(service)->sysv_enabled = true;
- }
-
- if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0)
- goto finish;
-
- if ((r = set_put(runlevel_services[i], service)) < 0)
- goto finish;
-
- } else if (de->d_name[0] == 'K' &&
- (rcnd_table[i].type == RUNLEVEL_DOWN ||
- rcnd_table[i].type == RUNLEVEL_SYSINIT)) {
-
- if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
- goto finish;
-
- if ((r = set_put(shutdown_services, service)) < 0)
- goto finish;
- }
- }
- }
-
- /* Now we loaded all stubs and are aware of the lowest
- start-up priority for all services, not let's actually load
- the services, this will also tell us which services are
- actually native now */
- manager_dispatch_load_queue(m);
-
- /* If this is a native service, rely on native ways to pull in
- * a service, don't pull it in via sysv rcN.d links. */
- for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
- SET_FOREACH(service, runlevel_services[i], j) {
- service = unit_follow_merge(service);
-
- if (service->fragment_path)
- continue;
-
- if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0)
- goto finish;
- }
-
- /* We honour K links only for halt/reboot. For the normal
- * runlevels we assume the stop jobs will be implicitly added
- * by the core logic. Also, we don't really distinguish here
- * between the runlevels 0 and 6 and just add them to the
- * special shutdown target. On SUSE the boot.d/ runlevel is
- * also used for shutdown, so we add links for that too to the
- * shutdown target.*/
- SET_FOREACH(service, shutdown_services, j) {
- service = unit_follow_merge(service);
-
- if (service->fragment_path)
- continue;
-
- if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
- goto finish;
- }
-
- r = 0;
-
-#ifdef TARGET_SUSE
- sysv_facility_in_insserv_conf (m);
-#endif
-
-finish:
- free(path);
- free(fpath);
- free(name);
-
- for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
- set_free(runlevel_services[i]);
- set_free(shutdown_services);
-
- if (d)
- closedir(d);
-
- return r;
-}
-#endif
-
-static void service_bus_name_owner_change(
- Unit *u,
- const char *name,
- const char *old_owner,
- const char *new_owner) {
-
- Service *s = SERVICE(u);
-
- assert(s);
- assert(name);
-
- assert(streq(s->bus_name, name));
- assert(old_owner || new_owner);
-
- if (old_owner && new_owner)
- log_debug("%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner);
- else if (old_owner)
- log_debug("%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner);
- else
- log_debug("%s's D-Bus name %s now registered by %s", u->id, name, new_owner);
-
- s->bus_name_good = !!new_owner;
-
- if (s->type == SERVICE_DBUS) {
-
- /* service_enter_running() will figure out what to
- * do */
- if (s->state == SERVICE_RUNNING)
- service_enter_running(s, SERVICE_SUCCESS);
- else if (s->state == SERVICE_START && new_owner)
- service_enter_start_post(s);
-
- } else if (new_owner &&
- s->main_pid <= 0 &&
- (s->state == SERVICE_START ||
- s->state == SERVICE_START_POST ||
- s->state == SERVICE_RUNNING ||
- s->state == SERVICE_RELOAD)) {
-
- /* Try to acquire PID from bus service */
- log_debug("Trying to acquire PID from D-Bus name...");
-
- bus_query_pid(u->manager, name);
- }
-}
-
-static void service_bus_query_pid_done(
- Unit *u,
- const char *name,
- pid_t pid) {
-
- Service *s = SERVICE(u);
-
- assert(s);
- assert(name);
-
- log_debug("%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid);
-
- if (s->main_pid <= 0 &&
- (s->state == SERVICE_START ||
- s->state == SERVICE_START_POST ||
- s->state == SERVICE_RUNNING ||
- s->state == SERVICE_RELOAD))
- service_set_main_pid(s, pid);
-}
-
-int service_set_socket_fd(Service *s, int fd, Socket *sock) {
-
- assert(s);
- assert(fd >= 0);
-
- /* This is called by the socket code when instantiating a new
- * service for a stream socket and the socket needs to be
- * configured. */
-
- if (UNIT(s)->load_state != UNIT_LOADED)
- return -EINVAL;
-
- if (s->socket_fd >= 0)
- return -EBUSY;
-
- if (s->state != SERVICE_DEAD)
- return -EAGAIN;
-
- s->socket_fd = fd;
- s->got_socket_fd = true;
-
- unit_ref_set(&s->accept_socket, UNIT(sock));
-
- return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
-}
-
-static void service_reset_failed(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- if (s->state == SERVICE_FAILED)
- service_set_state(s, SERVICE_DEAD);
-
- s->result = SERVICE_SUCCESS;
- s->reload_result = SERVICE_SUCCESS;
-}
-
-static bool service_need_daemon_reload(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
-#ifdef HAVE_SYSV_COMPAT
- if (s->sysv_path) {
- struct stat st;
-
- zero(st);
- if (stat(s->sysv_path, &st) < 0)
- /* What, cannot access this anymore? */
- return true;
-
- if (s->sysv_mtime > 0 &&
- timespec_load(&st.st_mtim) != s->sysv_mtime)
- return true;
- }
-#endif
-
- return false;
-}
-
-static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
- Service *s = SERVICE(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (s->main_pid <= 0 && who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_MAIN || who == KILL_ALL)
- if (s->main_pid > 0)
- if (kill(s->main_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
- int q;
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- /* Exclude the control/main pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
- r = q;
- goto finish;
- }
-
- if (s->main_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) {
- r = q;
- goto finish;
- }
-
- if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
-}
-
-static const char* const service_state_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = "dead",
- [SERVICE_START_PRE] = "start-pre",
- [SERVICE_START] = "start",
- [SERVICE_START_POST] = "start-post",
- [SERVICE_RUNNING] = "running",
- [SERVICE_EXITED] = "exited",
- [SERVICE_RELOAD] = "reload",
- [SERVICE_STOP] = "stop",
- [SERVICE_STOP_SIGTERM] = "stop-sigterm",
- [SERVICE_STOP_SIGKILL] = "stop-sigkill",
- [SERVICE_STOP_POST] = "stop-post",
- [SERVICE_FINAL_SIGTERM] = "final-sigterm",
- [SERVICE_FINAL_SIGKILL] = "final-sigkill",
- [SERVICE_FAILED] = "failed",
- [SERVICE_AUTO_RESTART] = "auto-restart",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
-
-static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
- [SERVICE_RESTART_NO] = "no",
- [SERVICE_RESTART_ON_SUCCESS] = "on-success",
- [SERVICE_RESTART_ON_FAILURE] = "on-failure",
- [SERVICE_RESTART_ON_ABORT] = "on-abort",
- [SERVICE_RESTART_ALWAYS] = "always"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
-
-static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
- [SERVICE_SIMPLE] = "simple",
- [SERVICE_FORKING] = "forking",
- [SERVICE_ONESHOT] = "oneshot",
- [SERVICE_DBUS] = "dbus",
- [SERVICE_NOTIFY] = "notify"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
-
-static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
- [SERVICE_EXEC_START_PRE] = "ExecStartPre",
- [SERVICE_EXEC_START] = "ExecStart",
- [SERVICE_EXEC_START_POST] = "ExecStartPost",
- [SERVICE_EXEC_RELOAD] = "ExecReload",
- [SERVICE_EXEC_STOP] = "ExecStop",
- [SERVICE_EXEC_STOP_POST] = "ExecStopPost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
-
-static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = {
- [NOTIFY_NONE] = "none",
- [NOTIFY_MAIN] = "main",
- [NOTIFY_ALL] = "all"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
-
-static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
- [SERVICE_SUCCESS] = "success",
- [SERVICE_FAILURE_RESOURCES] = "resources",
- [SERVICE_FAILURE_TIMEOUT] = "timeout",
- [SERVICE_FAILURE_EXIT_CODE] = "exit-code",
- [SERVICE_FAILURE_SIGNAL] = "signal",
- [SERVICE_FAILURE_CORE_DUMP] = "core-dump",
- [SERVICE_FAILURE_WATCHDOG] = "watchdog"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
-
-static const char* const start_limit_action_table[_SERVICE_START_LIMIT_MAX] = {
- [SERVICE_START_LIMIT_NONE] = "none",
- [SERVICE_START_LIMIT_REBOOT] = "reboot",
- [SERVICE_START_LIMIT_REBOOT_FORCE] = "reboot-force",
- [SERVICE_START_LIMIT_REBOOT_IMMEDIATE] = "reboot-immediate"
-};
-DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
-
-const UnitVTable service_vtable = {
- .suffix = ".service",
- .object_size = sizeof(Service),
- .sections =
- "Unit\0"
- "Service\0"
- "Install\0",
- .show_status = true,
-
- .init = service_init,
- .done = service_done,
- .load = service_load,
-
- .coldplug = service_coldplug,
-
- .dump = service_dump,
-
- .start = service_start,
- .stop = service_stop,
- .reload = service_reload,
-
- .can_reload = service_can_reload,
-
- .kill = service_kill,
-
- .serialize = service_serialize,
- .deserialize_item = service_deserialize_item,
-
- .active_state = service_active_state,
- .sub_state_to_string = service_sub_state_to_string,
-
- .check_gc = service_check_gc,
- .check_snapshot = service_check_snapshot,
-
- .sigchld_event = service_sigchld_event,
- .timer_event = service_timer_event,
- .fd_event = service_fd_event,
-
- .reset_failed = service_reset_failed,
-
- .need_daemon_reload = service_need_daemon_reload,
-
- .cgroup_notify_empty = service_cgroup_notify_event,
- .notify_message = service_notify_message,
-
- .bus_name_owner_change = service_bus_name_owner_change,
- .bus_query_pid_done = service_bus_query_pid_done,
-
- .bus_interface = "org.freedesktop.systemd1.Service",
- .bus_message_handler = bus_service_message_handler,
- .bus_invalidating_properties = bus_service_invalidating_properties,
-
-#ifdef HAVE_SYSV_COMPAT
- .enumerate = service_enumerate
-#endif
-};
diff --git a/src/service.h b/src/service.h
deleted file mode 100644
index 60b1051..0000000
--- a/src/service.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooservicehfoo
-#define fooservicehfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Service Service;
-
-#include "unit.h"
-#include "path.h"
-#include "ratelimit.h"
-#include "service.h"
-
-typedef enum ServiceState {
- SERVICE_DEAD,
- SERVICE_START_PRE,
- SERVICE_START,
- SERVICE_START_POST,
- SERVICE_RUNNING,
- SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
- SERVICE_RELOAD,
- SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
- SERVICE_STOP_SIGTERM,
- SERVICE_STOP_SIGKILL,
- SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
- SERVICE_FINAL_SIGKILL,
- SERVICE_FAILED,
- SERVICE_AUTO_RESTART,
- _SERVICE_STATE_MAX,
- _SERVICE_STATE_INVALID = -1
-} ServiceState;
-
-typedef enum ServiceRestart {
- SERVICE_RESTART_NO,
- SERVICE_RESTART_ON_SUCCESS,
- SERVICE_RESTART_ON_FAILURE,
- SERVICE_RESTART_ON_ABORT,
- SERVICE_RESTART_ALWAYS,
- _SERVICE_RESTART_MAX,
- _SERVICE_RESTART_INVALID = -1
-} ServiceRestart;
-
-typedef enum ServiceType {
- SERVICE_SIMPLE, /* we fork and go on right-away (i.e. modern socket activated daemons) */
- SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */
- SERVICE_ONESHOT, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */
- SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */
- SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */
- _SERVICE_TYPE_MAX,
- _SERVICE_TYPE_INVALID = -1
-} ServiceType;
-
-typedef enum ServiceExecCommand {
- SERVICE_EXEC_START_PRE,
- SERVICE_EXEC_START,
- SERVICE_EXEC_START_POST,
- SERVICE_EXEC_RELOAD,
- SERVICE_EXEC_STOP,
- SERVICE_EXEC_STOP_POST,
- _SERVICE_EXEC_COMMAND_MAX,
- _SERVICE_EXEC_COMMAND_INVALID = -1
-} ServiceExecCommand;
-
-typedef enum NotifyAccess {
- NOTIFY_NONE,
- NOTIFY_ALL,
- NOTIFY_MAIN,
- _NOTIFY_ACCESS_MAX,
- _NOTIFY_ACCESS_INVALID = -1
-} NotifyAccess;
-
-typedef enum ServiceResult {
- SERVICE_SUCCESS,
- SERVICE_FAILURE_RESOURCES,
- SERVICE_FAILURE_TIMEOUT,
- SERVICE_FAILURE_EXIT_CODE,
- SERVICE_FAILURE_SIGNAL,
- SERVICE_FAILURE_CORE_DUMP,
- SERVICE_FAILURE_WATCHDOG,
- _SERVICE_RESULT_MAX,
- _SERVICE_RESULT_INVALID = -1
-} ServiceResult;
-
-typedef enum StartLimitAction {
- SERVICE_START_LIMIT_NONE,
- SERVICE_START_LIMIT_REBOOT,
- SERVICE_START_LIMIT_REBOOT_FORCE,
- SERVICE_START_LIMIT_REBOOT_IMMEDIATE,
- _SERVICE_START_LIMIT_MAX,
- _SERVICE_START_LIMIT_INVALID = -1
-} StartLimitAction;
-
-struct Service {
- Unit meta;
-
- ServiceType type;
- ServiceRestart restart;
-
- /* If set we'll read the main daemon PID from this file */
- char *pid_file;
-
- usec_t restart_usec;
- usec_t timeout_usec;
-
- dual_timestamp watchdog_timestamp;
- usec_t watchdog_usec;
- Watch watchdog_watch;
-
- ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
- ExecContext exec_context;
-
- ServiceState state, deserialized_state;
-
- /* The exit status of the real main process */
- ExecStatus main_exec_status;
-
- /* The currently executed control process */
- ExecCommand *control_command;
-
- /* The currently executed main process, which may be NULL if
- * the main process got started via forking mode and not by
- * us */
- ExecCommand *main_command;
-
- /* The ID of the control command currently being executed */
- ServiceExecCommand control_command_id;
-
- pid_t main_pid, control_pid;
- int socket_fd;
-
- int fsck_passno;
-
- bool permissions_start_only;
- bool root_directory_start_only;
- bool remain_after_exit;
- bool guess_main_pid;
-
- /* If we shut down, remember why */
- ServiceResult result;
- ServiceResult reload_result;
-
- bool main_pid_known:1;
- bool main_pid_alien:1;
- bool bus_name_good:1;
- bool forbid_restart:1;
- bool got_socket_fd:1;
-#ifdef HAVE_SYSV_COMPAT
- bool sysv_has_lsb:1;
- bool sysv_enabled:1;
- int sysv_start_priority_from_rcnd;
- int sysv_start_priority;
-
- char *sysv_path;
- char *sysv_runlevels;
- usec_t sysv_mtime;
-#endif
-
- char *bus_name;
-
- char *status_text;
-
- RateLimit start_limit;
- StartLimitAction start_limit_action;
-
-
- UnitRef accept_socket;
-
- Watch timer_watch;
- PathSpec *pid_file_pathspec;
-
- NotifyAccess notify_access;
-};
-
-extern const UnitVTable service_vtable;
-
-struct Socket;
-
-int service_set_socket_fd(Service *s, int fd, struct Socket *socket);
-
-const char* service_state_to_string(ServiceState i);
-ServiceState service_state_from_string(const char *s);
-
-const char* service_restart_to_string(ServiceRestart i);
-ServiceRestart service_restart_from_string(const char *s);
-
-const char* service_type_to_string(ServiceType i);
-ServiceType service_type_from_string(const char *s);
-
-const char* service_exec_command_to_string(ServiceExecCommand i);
-ServiceExecCommand service_exec_command_from_string(const char *s);
-
-const char* notify_access_to_string(NotifyAccess i);
-NotifyAccess notify_access_from_string(const char *s);
-
-const char* service_result_to_string(ServiceResult i);
-ServiceResult service_result_from_string(const char *s);
-
-const char* start_limit_action_to_string(StartLimitAction i);
-StartLimitAction start_limit_action_from_string(const char *s);
-
-#endif
diff --git a/src/snapshot.c b/src/snapshot.c
deleted file mode 100644
index 82ec510..0000000
--- a/src/snapshot.c
+++ /dev/null
@@ -1,309 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "unit.h"
-#include "snapshot.h"
-#include "unit-name.h"
-#include "dbus-snapshot.h"
-#include "bus-errors.h"
-
-static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = UNIT_INACTIVE,
- [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
-};
-
-static void snapshot_init(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(UNIT(s)->load_state == UNIT_STUB);
-
- UNIT(s)->ignore_on_isolate = true;
- UNIT(s)->ignore_on_snapshot = true;
-}
-
-static void snapshot_set_state(Snapshot *s, SnapshotState state) {
- SnapshotState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(s)->id,
- snapshot_state_to_string(old_state),
- snapshot_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int snapshot_load(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- /* Make sure that only snapshots created via snapshot_create()
- * can be loaded */
- if (!s->by_snapshot_create && UNIT(s)->manager->n_reloading <= 0)
- return -ENOENT;
-
- u->load_state = UNIT_LOADED;
- return 0;
-}
-
-static int snapshot_coldplug(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- if (s->deserialized_state != s->state)
- snapshot_set_state(s, s->deserialized_state);
-
- return 0;
-}
-
-static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(f);
-
- fprintf(f,
- "%sSnapshot State: %s\n"
- "%sClean Up: %s\n",
- prefix, snapshot_state_to_string(s->state),
- prefix, yes_no(s->cleanup));
-}
-
-static int snapshot_start(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- snapshot_set_state(s, SNAPSHOT_ACTIVE);
-
- if (s->cleanup)
- unit_add_to_cleanup_queue(u);
-
- return 0;
-}
-
-static int snapshot_stop(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_ACTIVE);
-
- snapshot_set_state(s, SNAPSHOT_DEAD);
- return 0;
-}
-
-static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- Unit *other;
- Iterator i;
-
- assert(s);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
- unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
- unit_serialize_item(u, f, "wants", other->id);
-
- return 0;
-}
-
-static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- int r;
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- SnapshotState state;
-
- if ((state = snapshot_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- s->deserialized_state = state;
-
- } else if (streq(key, "cleanup")) {
-
- if ((r = parse_boolean(value)) < 0)
- log_debug("Failed to parse cleanup value %s", value);
- else
- s->cleanup = r;
-
- } else if (streq(key, "wants")) {
-
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true)) < 0)
- return r;
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState snapshot_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SNAPSHOT(u)->state];
-}
-
-static const char *snapshot_sub_state_to_string(Unit *u) {
- assert(u);
-
- return snapshot_state_to_string(SNAPSHOT(u)->state);
-}
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **_s) {
- Iterator i;
- Unit *other, *u = NULL;
- char *n = NULL;
- int r;
- const char *k;
-
- assert(m);
- assert(_s);
-
- if (name) {
- if (!unit_name_is_valid(name, false)) {
- dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name);
- return -EINVAL;
- }
-
- if (unit_name_to_type(name) != UNIT_SNAPSHOT) {
- dbus_set_error(e, BUS_ERROR_UNIT_TYPE_MISMATCH, "Unit name %s lacks snapshot suffix.", name);
- return -EINVAL;
- }
-
- if (manager_get_unit(m, name)) {
- dbus_set_error(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
- return -EEXIST;
- }
-
- } else {
-
- for (;;) {
- if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
- return -ENOMEM;
-
- if (!manager_get_unit(m, n))
- break;
-
- free(n);
- }
-
- name = n;
- }
-
- r = manager_load_unit_prepare(m, name, NULL, e, &u);
- free(n);
-
- if (r < 0)
- goto fail;
-
- SNAPSHOT(u)->by_snapshot_create = true;
- manager_dispatch_load_queue(m);
- assert(u->load_state == UNIT_LOADED);
-
- HASHMAP_FOREACH_KEY(other, k, m->units, i) {
-
- if (other->ignore_on_snapshot)
- continue;
-
- if (k != other->id)
- continue;
-
- if (UNIT_VTABLE(other)->check_snapshot)
- if (!UNIT_VTABLE(other)->check_snapshot(other))
- continue;
-
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- continue;
-
- if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true)) < 0)
- goto fail;
- }
-
- SNAPSHOT(u)->cleanup = cleanup;
- *_s = SNAPSHOT(u);
-
- return 0;
-
-fail:
- if (u)
- unit_add_to_cleanup_queue(u);
-
- return r;
-}
-
-void snapshot_remove(Snapshot *s) {
- assert(s);
-
- unit_add_to_cleanup_queue(UNIT(s));
-}
-
-static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = "dead",
- [SNAPSHOT_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
-
-const UnitVTable snapshot_vtable = {
- .suffix = ".snapshot",
- .object_size = sizeof(Snapshot),
-
- .no_alias = true,
- .no_instances = true,
- .no_gc = true,
-
- .init = snapshot_init,
-
- .load = snapshot_load,
- .coldplug = snapshot_coldplug,
-
- .dump = snapshot_dump,
-
- .start = snapshot_start,
- .stop = snapshot_stop,
-
- .serialize = snapshot_serialize,
- .deserialize_item = snapshot_deserialize_item,
-
- .active_state = snapshot_active_state,
- .sub_state_to_string = snapshot_sub_state_to_string,
-
- .bus_interface = "org.freedesktop.systemd1.Snapshot",
- .bus_message_handler = bus_snapshot_message_handler
-};
diff --git a/src/snapshot.h b/src/snapshot.h
deleted file mode 100644
index bf92e99..0000000
--- a/src/snapshot.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosnapshothfoo
-#define foosnapshothfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Snapshot Snapshot;
-
-#include "unit.h"
-
-typedef enum SnapshotState {
- SNAPSHOT_DEAD,
- SNAPSHOT_ACTIVE,
- _SNAPSHOT_STATE_MAX,
- _SNAPSHOT_STATE_INVALID = -1
-} SnapshotState;
-
-struct Snapshot {
- Unit meta;
-
- SnapshotState state, deserialized_state;
-
- bool cleanup;
- bool by_snapshot_create:1;
-};
-
-extern const UnitVTable snapshot_vtable;
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s);
-void snapshot_remove(Snapshot *s);
-
-const char* snapshot_state_to_string(SnapshotState i);
-SnapshotState snapshot_state_from_string(const char *s);
-
-#endif
diff --git a/src/socket.c b/src/socket.c
deleted file mode 100644
index 5b24b34..0000000
--- a/src/socket.c
+++ /dev/null
@@ -1,2218 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <signal.h>
-#include <arpa/inet.h>
-#include <mqueue.h>
-
-#include "unit.h"
-#include "socket.h"
-#include "netinet/tcp.h"
-#include "log.h"
-#include "load-dropin.h"
-#include "load-fragment.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "unit-name.h"
-#include "dbus-socket.h"
-#include "missing.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "label.h"
-#include "exit-status.h"
-#include "def.h"
-
-static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = UNIT_INACTIVE,
- [SOCKET_START_PRE] = UNIT_ACTIVATING,
- [SOCKET_START_POST] = UNIT_ACTIVATING,
- [SOCKET_LISTENING] = UNIT_ACTIVE,
- [SOCKET_RUNNING] = UNIT_ACTIVE,
- [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
- [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING,
- [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING,
- [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
- [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
- [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
- [SOCKET_FAILED] = UNIT_FAILED
-};
-
-static void socket_init(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- s->backlog = SOMAXCONN;
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
- s->directory_mode = 0755;
- s->socket_mode = 0666;
-
- s->max_connections = 64;
-
- s->priority = -1;
- s->ip_tos = -1;
- s->ip_ttl = -1;
- s->mark = -1;
-
- exec_context_init(&s->exec_context);
- s->exec_context.std_output = u->manager->default_std_output;
- s->exec_context.std_error = u->manager->default_std_error;
-
- s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
-}
-
-static void socket_unwatch_control_pid(Socket *s) {
- assert(s);
-
- if (s->control_pid <= 0)
- return;
-
- unit_unwatch_pid(UNIT(s), s->control_pid);
- s->control_pid = 0;
-}
-
-static void socket_done(Unit *u) {
- Socket *s = SOCKET(u);
- SocketPort *p;
-
- assert(s);
-
- while ((p = s->ports)) {
- LIST_REMOVE(SocketPort, port, s->ports, p);
-
- if (p->fd >= 0) {
- unit_unwatch_fd(UNIT(s), &p->fd_watch);
- close_nointr_nofail(p->fd);
- }
-
- free(p->path);
- free(p);
- }
-
- exec_context_done(&s->exec_context);
- exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
- s->control_command = NULL;
-
- socket_unwatch_control_pid(s);
-
- unit_ref_unset(&s->service);
-
- free(s->tcp_congestion);
- s->tcp_congestion = NULL;
-
- free(s->bind_to_device);
- s->bind_to_device = NULL;
-
- unit_unwatch_timer(u, &s->timer_watch);
-}
-
-static int socket_instantiate_service(Socket *s) {
- char *prefix, *name;
- int r;
- Unit *u;
-
- assert(s);
-
- /* This fills in s->service if it isn't filled in yet. For
- * Accept=yes sockets we create the next connection service
- * here. For Accept=no this is mostly a NOP since the service
- * is figured out at load time anyway. */
-
- if (UNIT_DEREF(s->service))
- return 0;
-
- assert(s->accept);
-
- if (!(prefix = unit_name_to_prefix(UNIT(s)->id)))
- return -ENOMEM;
-
- r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted);
- free(prefix);
-
- if (r < 0)
- return -ENOMEM;
-
- r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u);
- free(name);
-
- if (r < 0)
- return r;
-
-#ifdef HAVE_SYSV_COMPAT
- if (SERVICE(u)->sysv_path) {
- log_error("Using SysV services for socket activation is not supported. Refusing.");
- return -ENOENT;
- }
-#endif
-
- u->no_gc = true;
- unit_ref_set(&s->service, u);
-
- return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
-}
-
-static bool have_non_accept_socket(Socket *s) {
- SocketPort *p;
-
- assert(s);
-
- if (!s->accept)
- return true;
-
- LIST_FOREACH(port, p, s->ports) {
-
- if (p->type != SOCKET_SOCKET)
- return true;
-
- if (!socket_address_can_accept(&p->address))
- return true;
- }
-
- return false;
-}
-
-static int socket_verify(Socket *s) {
- assert(s);
-
- if (UNIT(s)->load_state != UNIT_LOADED)
- return 0;
-
- if (!s->ports) {
- log_error("%s lacks Listen setting. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->accept && have_non_accept_socket(s)) {
- log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->accept && s->max_connections <= 0) {
- log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->accept && UNIT_DEREF(s->service)) {
- log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static bool socket_needs_mount(Socket *s, const char *prefix) {
- SocketPort *p;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
-
- if (p->type == SOCKET_SOCKET) {
- if (socket_address_needs_mount(&p->address, prefix))
- return true;
- } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) {
- if (path_startswith(p->path, prefix))
- return true;
- }
- }
-
- return false;
-}
-
-int socket_add_one_mount_link(Socket *s, Mount *m) {
- int r;
-
- assert(s);
- assert(m);
-
- if (UNIT(s)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (!socket_needs_mount(s, m->where))
- return 0;
-
- if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
-
- return 0;
-}
-
-static int socket_add_mount_links(Socket *s) {
- Unit *other;
- int r;
-
- assert(s);
-
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
- if ((r = socket_add_one_mount_link(s, MOUNT(other))) < 0)
- return r;
-
- return 0;
-}
-
-static int socket_add_device_link(Socket *s) {
- char *t;
- int r;
-
- assert(s);
-
- if (!s->bind_to_device)
- return 0;
-
- if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0)
- return -ENOMEM;
-
- r = unit_add_node_link(UNIT(s), t, false);
- free(t);
-
- return r;
-}
-
-static int socket_add_default_dependencies(Socket *s) {
- int r;
- assert(s);
-
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
- return r;
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
- return r;
- }
-
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
-}
-
-static bool socket_has_exec(Socket *s) {
- unsigned i;
- assert(s);
-
- for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++)
- if (s->exec_command[i])
- return true;
-
- return false;
-}
-
-static int socket_load(Unit *u) {
- Socket *s = SOCKET(u);
- int r;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
- return r;
-
- /* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
-
- if (have_non_accept_socket(s)) {
-
- if (!UNIT_DEREF(s->service)) {
- Unit *x;
-
- r = unit_load_related_unit(u, ".service", &x);
- if (r < 0)
- return r;
-
- unit_ref_set(&s->service, x);
- }
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true);
- if (r < 0)
- return r;
- }
-
- if ((r = socket_add_mount_links(s)) < 0)
- return r;
-
- if ((r = socket_add_device_link(s)) < 0)
- return r;
-
- if (socket_has_exec(s))
- if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
- return r;
-
- if ((r = unit_add_default_cgroups(u)) < 0)
- return r;
-
- if (UNIT(s)->default_dependencies)
- if ((r = socket_add_default_dependencies(s)) < 0)
- return r;
- }
-
- return socket_verify(s);
-}
-
-static const char* listen_lookup(int family, int type) {
-
- if (family == AF_NETLINK)
- return "ListenNetlink";
-
- if (type == SOCK_STREAM)
- return "ListenStream";
- else if (type == SOCK_DGRAM)
- return "ListenDatagram";
- else if (type == SOCK_SEQPACKET)
- return "ListenSequentialPacket";
-
- assert_not_reached("Unknown socket type");
- return NULL;
-}
-
-static void socket_dump(Unit *u, FILE *f, const char *prefix) {
-
- SocketExecCommand c;
- Socket *s = SOCKET(u);
- SocketPort *p;
- const char *prefix2;
- char *p2;
-
- assert(s);
- assert(f);
-
- p2 = strappend(prefix, "\t");
- prefix2 = p2 ? p2 : prefix;
-
- fprintf(f,
- "%sSocket State: %s\n"
- "%sResult: %s\n"
- "%sBindIPv6Only: %s\n"
- "%sBacklog: %u\n"
- "%sSocketMode: %04o\n"
- "%sDirectoryMode: %04o\n"
- "%sKeepAlive: %s\n"
- "%sFreeBind: %s\n"
- "%sTransparent: %s\n"
- "%sBroadcast: %s\n"
- "%sPassCredentials: %s\n"
- "%sPassSecurity: %s\n"
- "%sTCPCongestion: %s\n",
- prefix, socket_state_to_string(s->state),
- prefix, socket_result_to_string(s->result),
- prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only),
- prefix, s->backlog,
- prefix, s->socket_mode,
- prefix, s->directory_mode,
- prefix, yes_no(s->keep_alive),
- prefix, yes_no(s->free_bind),
- prefix, yes_no(s->transparent),
- prefix, yes_no(s->broadcast),
- prefix, yes_no(s->pass_cred),
- prefix, yes_no(s->pass_sec),
- prefix, strna(s->tcp_congestion));
-
- if (s->control_pid > 0)
- fprintf(f,
- "%sControl PID: %lu\n",
- prefix, (unsigned long) s->control_pid);
-
- if (s->bind_to_device)
- fprintf(f,
- "%sBindToDevice: %s\n",
- prefix, s->bind_to_device);
-
- if (s->accept)
- fprintf(f,
- "%sAccepted: %u\n"
- "%sNConnections: %u\n"
- "%sMaxConnections: %u\n",
- prefix, s->n_accepted,
- prefix, s->n_connections,
- prefix, s->max_connections);
-
- if (s->priority >= 0)
- fprintf(f,
- "%sPriority: %i\n",
- prefix, s->priority);
-
- if (s->receive_buffer > 0)
- fprintf(f,
- "%sReceiveBuffer: %zu\n",
- prefix, s->receive_buffer);
-
- if (s->send_buffer > 0)
- fprintf(f,
- "%sSendBuffer: %zu\n",
- prefix, s->send_buffer);
-
- if (s->ip_tos >= 0)
- fprintf(f,
- "%sIPTOS: %i\n",
- prefix, s->ip_tos);
-
- if (s->ip_ttl >= 0)
- fprintf(f,
- "%sIPTTL: %i\n",
- prefix, s->ip_ttl);
-
- if (s->pipe_size > 0)
- fprintf(f,
- "%sPipeSize: %zu\n",
- prefix, s->pipe_size);
-
- if (s->mark >= 0)
- fprintf(f,
- "%sMark: %i\n",
- prefix, s->mark);
-
- if (s->mq_maxmsg > 0)
- fprintf(f,
- "%sMessageQueueMaxMessages: %li\n",
- prefix, s->mq_maxmsg);
-
- if (s->mq_msgsize > 0)
- fprintf(f,
- "%sMessageQueueMessageSize: %li\n",
- prefix, s->mq_msgsize);
-
- LIST_FOREACH(port, p, s->ports) {
-
- if (p->type == SOCKET_SOCKET) {
- const char *t;
- int r;
- char *k = NULL;
-
- if ((r = socket_address_print(&p->address, &k)) < 0)
- t = strerror(-r);
- else
- t = k;
-
- fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t);
- free(k);
- } else if (p->type == SOCKET_SPECIAL)
- fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
- else if (p->type == SOCKET_MQUEUE)
- fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
- else
- fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
- }
-
- exec_context_dump(&s->exec_context, f, prefix);
-
- for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
- if (!s->exec_command[c])
- continue;
-
- fprintf(f, "%s-> %s:\n",
- prefix, socket_exec_command_to_string(c));
-
- exec_command_dump_list(s->exec_command[c], f, prefix2);
- }
-
- free(p2);
-}
-
-static int instance_from_socket(int fd, unsigned nr, char **instance) {
- socklen_t l;
- char *r;
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- struct sockaddr_storage storage;
- } local, remote;
-
- assert(fd >= 0);
- assert(instance);
-
- l = sizeof(local);
- if (getsockname(fd, &local.sa, &l) < 0)
- return -errno;
-
- l = sizeof(remote);
- if (getpeername(fd, &remote.sa, &l) < 0)
- return -errno;
-
- switch (local.sa.sa_family) {
-
- case AF_INET: {
- uint32_t
- a = ntohl(local.in.sin_addr.s_addr),
- b = ntohl(remote.in.sin_addr.s_addr);
-
- if (asprintf(&r,
- "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
- nr,
- a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
- ntohs(local.in.sin_port),
- b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
- ntohs(remote.in.sin_port)) < 0)
- return -ENOMEM;
-
- break;
- }
-
- case AF_INET6: {
- static const char ipv4_prefix[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
- };
-
- if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 &&
- memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
- const uint8_t
- *a = local.in6.sin6_addr.s6_addr+12,
- *b = remote.in6.sin6_addr.s6_addr+12;
-
- if (asprintf(&r,
- "%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
- nr,
- a[0], a[1], a[2], a[3],
- ntohs(local.in6.sin6_port),
- b[0], b[1], b[2], b[3],
- ntohs(remote.in6.sin6_port)) < 0)
- return -ENOMEM;
- } else {
- char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
-
- if (asprintf(&r,
- "%u-%s:%u-%s:%u",
- nr,
- inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
- ntohs(local.in6.sin6_port),
- inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
- ntohs(remote.in6.sin6_port)) < 0)
- return -ENOMEM;
- }
-
- break;
- }
-
- case AF_UNIX: {
- struct ucred ucred;
-
- l = sizeof(ucred);
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0)
- return -errno;
-
- if (asprintf(&r,
- "%u-%lu-%lu",
- nr,
- (unsigned long) ucred.pid,
- (unsigned long) ucred.uid) < 0)
- return -ENOMEM;
-
- break;
- }
-
- default:
- assert_not_reached("Unhandled socket type.");
- }
-
- *instance = r;
- return 0;
-}
-
-static void socket_close_fds(Socket *s) {
- SocketPort *p;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
- if (p->fd < 0)
- continue;
-
- unit_unwatch_fd(UNIT(s), &p->fd_watch);
- close_nointr_nofail(p->fd);
-
- /* One little note: we should never delete any sockets
- * in the file system here! After all some other
- * process we spawned might still have a reference of
- * this fd and wants to continue to use it. Therefore
- * we delete sockets in the file system before we
- * create a new one, not after we stopped using
- * one! */
-
- p->fd = -1;
- }
-}
-
-static void socket_apply_socket_options(Socket *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- if (s->keep_alive) {
- int b = s->keep_alive;
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
- log_warning("SO_KEEPALIVE failed: %m");
- }
-
- if (s->broadcast) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
- log_warning("SO_BROADCAST failed: %m");
- }
-
- if (s->pass_cred) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- log_warning("SO_PASSCRED failed: %m");
- }
-
- if (s->pass_sec) {
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
- log_warning("SO_PASSSEC failed: %m");
- }
-
- if (s->priority >= 0)
- if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
- log_warning("SO_PRIORITY failed: %m");
-
- if (s->receive_buffer > 0) {
- int value = (int) s->receive_buffer;
-
- /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
-
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- log_warning("SO_RCVBUF failed: %m");
- }
-
- if (s->send_buffer > 0) {
- int value = (int) s->send_buffer;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
- if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- log_warning("SO_SNDBUF failed: %m");
- }
-
- if (s->mark >= 0)
- if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
- log_warning("SO_MARK failed: %m");
-
- if (s->ip_tos >= 0)
- if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
- log_warning("IP_TOS failed: %m");
-
- if (s->ip_ttl >= 0) {
- int r, x;
-
- r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
-
- if (socket_ipv6_is_supported())
- x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
- else {
- x = -1;
- errno = EAFNOSUPPORT;
- }
-
- if (r < 0 && x < 0)
- log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m");
- }
-
- if (s->tcp_congestion)
- if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
- log_warning("TCP_CONGESTION failed: %m");
-}
-
-static void socket_apply_fifo_options(Socket *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- if (s->pipe_size > 0)
- if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0)
- log_warning("F_SETPIPE_SZ: %m");
-}
-
-static int fifo_address_create(
- const char *path,
- mode_t directory_mode,
- mode_t socket_mode,
- int *_fd) {
-
- int fd = -1, r = 0;
- struct stat st;
- mode_t old_mask;
-
- assert(path);
- assert(_fd);
-
- mkdir_parents(path, directory_mode);
-
- if ((r = label_fifofile_set(path)) < 0)
- goto fail;
-
- /* Enforce the right access mode for the fifo */
- old_mask = umask(~ socket_mode);
-
- /* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
-
- r = mkfifo(path, socket_mode);
- umask(old_mask);
-
- if (r < 0 && errno != EEXIST) {
- r = -errno;
- goto fail;
- }
-
- if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
- r = -errno;
- goto fail;
- }
-
- label_file_clear();
-
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
-
- if (!S_ISFIFO(st.st_mode) ||
- (st.st_mode & 0777) != (socket_mode & ~old_mask) ||
- st.st_uid != getuid() ||
- st.st_gid != getgid()) {
-
- r = -EEXIST;
- goto fail;
- }
-
- *_fd = fd;
- return 0;
-
-fail:
- label_file_clear();
-
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int special_address_create(
- const char *path,
- int *_fd) {
-
- int fd = -1, r = 0;
- struct stat st;
-
- assert(path);
- assert(_fd);
-
- if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
- r = -errno;
- goto fail;
- }
-
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Check whether this is a /proc, /sys or /dev file or char device */
- if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
- r = -EEXIST;
- goto fail;
- }
-
- *_fd = fd;
- return 0;
-
-fail:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int mq_address_create(
- const char *path,
- mode_t mq_mode,
- long maxmsg,
- long msgsize,
- int *_fd) {
-
- int fd = -1, r = 0;
- struct stat st;
- mode_t old_mask;
- struct mq_attr _attr, *attr = NULL;
-
- assert(path);
- assert(_fd);
-
- if (maxmsg > 0 && msgsize > 0) {
- zero(_attr);
- _attr.mq_flags = O_NONBLOCK;
- _attr.mq_maxmsg = maxmsg;
- _attr.mq_msgsize = msgsize;
- attr = &_attr;
- }
-
- /* Enforce the right access mode for the mq */
- old_mask = umask(~ mq_mode);
-
- /* Include the original umask in our mask */
- umask(~mq_mode | old_mask);
-
- fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
- umask(old_mask);
-
- if (fd < 0) {
- r = -errno;
- goto fail;
- }
-
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
-
- if ((st.st_mode & 0777) != (mq_mode & ~old_mask) ||
- st.st_uid != getuid() ||
- st.st_gid != getgid()) {
-
- r = -EEXIST;
- goto fail;
- }
-
- *_fd = fd;
- return 0;
-
-fail:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int socket_open_fds(Socket *s) {
- SocketPort *p;
- int r;
- char *label = NULL;
- bool know_label = false;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
-
- if (p->fd >= 0)
- continue;
-
- if (p->type == SOCKET_SOCKET) {
-
- if (!know_label) {
-
- if ((r = socket_instantiate_service(s)) < 0)
- return r;
-
- if (UNIT_DEREF(s->service) &&
- SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
- r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
-
- if (r < 0) {
- if (r != -EPERM)
- return r;
- }
- }
-
- know_label = true;
- }
-
- if ((r = socket_address_listen(
- &p->address,
- s->backlog,
- s->bind_ipv6_only,
- s->bind_to_device,
- s->free_bind,
- s->transparent,
- s->directory_mode,
- s->socket_mode,
- label,
- &p->fd)) < 0)
- goto rollback;
-
- socket_apply_socket_options(s, p->fd);
-
- } else if (p->type == SOCKET_SPECIAL) {
-
- if ((r = special_address_create(
- p->path,
- &p->fd)) < 0)
- goto rollback;
-
- } else if (p->type == SOCKET_FIFO) {
-
- if ((r = fifo_address_create(
- p->path,
- s->directory_mode,
- s->socket_mode,
- &p->fd)) < 0)
- goto rollback;
-
- socket_apply_fifo_options(s, p->fd);
- } else if (p->type == SOCKET_MQUEUE) {
-
- if ((r = mq_address_create(
- p->path,
- s->socket_mode,
- s->mq_maxmsg,
- s->mq_msgsize,
- &p->fd)) < 0)
- goto rollback;
- } else
- assert_not_reached("Unknown port type");
- }
-
- label_free(label);
- return 0;
-
-rollback:
- socket_close_fds(s);
- label_free(label);
- return r;
-}
-
-static void socket_unwatch_fds(Socket *s) {
- SocketPort *p;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
- if (p->fd < 0)
- continue;
-
- unit_unwatch_fd(UNIT(s), &p->fd_watch);
- }
-}
-
-static int socket_watch_fds(Socket *s) {
- SocketPort *p;
- int r;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
- if (p->fd < 0)
- continue;
-
- p->fd_watch.socket_accept =
- s->accept &&
- p->type == SOCKET_SOCKET &&
- socket_address_can_accept(&p->address);
-
- if ((r = unit_watch_fd(UNIT(s), p->fd, EPOLLIN, &p->fd_watch)) < 0)
- goto fail;
- }
-
- return 0;
-
-fail:
- socket_unwatch_fds(s);
- return r;
-}
-
-static void socket_set_state(Socket *s, SocketState state) {
- SocketState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != SOCKET_START_PRE &&
- state != SOCKET_START_POST &&
- state != SOCKET_STOP_PRE &&
- state != SOCKET_STOP_PRE_SIGTERM &&
- state != SOCKET_STOP_PRE_SIGKILL &&
- state != SOCKET_STOP_POST &&
- state != SOCKET_FINAL_SIGTERM &&
- state != SOCKET_FINAL_SIGKILL) {
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
- socket_unwatch_control_pid(s);
- s->control_command = NULL;
- s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
- }
-
- if (state != SOCKET_LISTENING)
- socket_unwatch_fds(s);
-
- if (state != SOCKET_START_POST &&
- state != SOCKET_LISTENING &&
- state != SOCKET_RUNNING &&
- state != SOCKET_STOP_PRE &&
- state != SOCKET_STOP_PRE_SIGTERM &&
- state != SOCKET_STOP_PRE_SIGKILL)
- socket_close_fds(s);
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(s)->id,
- socket_state_to_string(old_state),
- socket_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int socket_coldplug(Unit *u) {
- Socket *s = SOCKET(u);
- int r;
-
- assert(s);
- assert(s->state == SOCKET_DEAD);
-
- if (s->deserialized_state != s->state) {
-
- if (s->deserialized_state == SOCKET_START_PRE ||
- s->deserialized_state == SOCKET_START_POST ||
- s->deserialized_state == SOCKET_STOP_PRE ||
- s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
- s->deserialized_state == SOCKET_STOP_PRE_SIGKILL ||
- s->deserialized_state == SOCKET_STOP_POST ||
- s->deserialized_state == SOCKET_FINAL_SIGTERM ||
- s->deserialized_state == SOCKET_FINAL_SIGKILL) {
-
- if (s->control_pid <= 0)
- return -EBADMSG;
-
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
- return r;
-
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- return r;
- }
-
- if (s->deserialized_state == SOCKET_START_POST ||
- s->deserialized_state == SOCKET_LISTENING ||
- s->deserialized_state == SOCKET_RUNNING ||
- s->deserialized_state == SOCKET_STOP_PRE ||
- s->deserialized_state == SOCKET_STOP_PRE_SIGTERM ||
- s->deserialized_state == SOCKET_STOP_PRE_SIGKILL)
- if ((r = socket_open_fds(s)) < 0)
- return r;
-
- if (s->deserialized_state == SOCKET_LISTENING)
- if ((r = socket_watch_fds(s)) < 0)
- return r;
-
- socket_set_state(s, s->deserialized_state);
- }
-
- return 0;
-}
-
-static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
- pid_t pid;
- int r;
- char **argv;
-
- assert(s);
- assert(c);
- assert(_pid);
-
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
-
- if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = exec_spawn(c,
- argv,
- &s->exec_context,
- NULL, 0,
- UNIT(s)->manager->environment,
- true,
- true,
- true,
- UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- &pid);
-
- strv_free(argv);
- if (r < 0)
- goto fail;
-
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
- /* FIXME: we need to do something here */
- goto fail;
-
- *_pid = pid;
-
- return 0;
-
-fail:
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
-
- return r;
-}
-
-static void socket_enter_dead(Socket *s, SocketResult f) {
- assert(s);
-
- if (f != SOCKET_SUCCESS)
- s->result = f;
-
- socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
-}
-
-static void socket_enter_signal(Socket *s, SocketState state, SocketResult f);
-
-static void socket_enter_stop_post(Socket *s, SocketResult f) {
- int r;
- assert(s);
-
- if (f != SOCKET_SUCCESS)
- s->result = f;
-
- socket_unwatch_control_pid(s);
-
- s->control_command_id = SOCKET_EXEC_STOP_POST;
-
- if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST])) {
- if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- socket_set_state(s, SOCKET_STOP_POST);
- } else
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
- int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
-
- assert(s);
-
- if (f != SOCKET_SUCCESS)
- s->result = f;
-
- if (s->exec_context.kill_mode != KILL_NONE) {
- int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning("Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
-
- if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
-
- socket_set_state(s, state);
- } else if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
- socket_enter_stop_post(s, SOCKET_SUCCESS);
- else
- socket_enter_dead(s, SOCKET_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
-
- if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
- socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
- else
- socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
-}
-
-static void socket_enter_stop_pre(Socket *s, SocketResult f) {
- int r;
- assert(s);
-
- if (f != SOCKET_SUCCESS)
- s->result = f;
-
- socket_unwatch_control_pid(s);
-
- s->control_command_id = SOCKET_EXEC_STOP_PRE;
-
- if ((s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE])) {
- if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- socket_set_state(s, SOCKET_STOP_PRE);
- } else
- socket_enter_stop_post(s, SOCKET_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r));
- socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_enter_listening(Socket *s) {
- int r;
- assert(s);
-
- r = socket_watch_fds(s);
- if (r < 0) {
- log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
- goto fail;
- }
-
- socket_set_state(s, SOCKET_LISTENING);
- return;
-
-fail:
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_enter_start_post(Socket *s) {
- int r;
- assert(s);
-
- r = socket_open_fds(s);
- if (r < 0) {
- log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r));
- goto fail;
- }
-
- socket_unwatch_control_pid(s);
-
- s->control_command_id = SOCKET_EXEC_START_POST;
-
- if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
- r = socket_spawn(s, s->control_command, &s->control_pid);
- if (r < 0) {
- log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
- goto fail;
- }
-
- socket_set_state(s, SOCKET_START_POST);
- } else
- socket_enter_listening(s);
-
- return;
-
-fail:
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_enter_start_pre(Socket *s) {
- int r;
- assert(s);
-
- socket_unwatch_control_pid(s);
-
- s->control_command_id = SOCKET_EXEC_START_PRE;
-
- if ((s->control_command = s->exec_command[SOCKET_EXEC_START_PRE])) {
- if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- socket_set_state(s, SOCKET_START_PRE);
- } else
- socket_enter_start_post(s);
-
- return;
-
-fail:
- log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
- socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_enter_running(Socket *s, int cfd) {
- int r;
- DBusError error;
-
- assert(s);
- dbus_error_init(&error);
-
- /* We don't take connections anymore if we are supposed to
- * shut down anyway */
- if (unit_pending_inactive(UNIT(s))) {
- log_debug("Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
-
- if (cfd >= 0)
- close_nointr_nofail(cfd);
- else {
- /* Flush all sockets by closing and reopening them */
- socket_close_fds(s);
-
- r = socket_watch_fds(s);
- if (r < 0) {
- log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- }
- }
-
- return;
- }
-
- if (cfd < 0) {
- Iterator i;
- Unit *u;
- bool pending = false;
-
- /* If there's already a start pending don't bother to
- * do anything */
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
- if (unit_pending_active(u)) {
- pending = true;
- break;
- }
-
- if (!pending) {
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- goto fail;
- }
-
- socket_set_state(s, SOCKET_RUNNING);
- } else {
- char *prefix, *instance = NULL, *name;
- Service *service;
-
- if (s->n_connections >= s->max_connections) {
- log_warning("Too many incoming connections (%u)", s->n_connections);
- close_nointr_nofail(cfd);
- return;
- }
-
- r = socket_instantiate_service(s);
- if (r < 0)
- goto fail;
-
- r = instance_from_socket(cfd, s->n_accepted, &instance);
- if (r < 0) {
- if (r != -ENOTCONN)
- goto fail;
-
- /* ENOTCONN is legitimate if TCP RST was received.
- * This connection is over, but the socket unit lives on. */
- close_nointr_nofail(cfd);
- return;
- }
-
- prefix = unit_name_to_prefix(UNIT(s)->id);
- if (!prefix) {
- free(instance);
- r = -ENOMEM;
- goto fail;
- }
-
- name = unit_name_build(prefix, instance, ".service");
- free(prefix);
- free(instance);
-
- if (!name) {
- r = -ENOMEM;
- goto fail;
- }
-
- r = unit_add_name(UNIT_DEREF(s->service), name);
- if (r < 0) {
- free(name);
- goto fail;
- }
-
- service = SERVICE(UNIT_DEREF(s->service));
- unit_ref_unset(&s->service);
- s->n_accepted ++;
-
- UNIT(service)->no_gc = false;
-
- unit_choose_id(UNIT(service), name);
- free(name);
-
- r = service_set_socket_fd(service, cfd, s);
- if (r < 0)
- goto fail;
-
- cfd = -1;
- s->n_connections ++;
-
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- goto fail;
-
- /* Notify clients about changed counters */
- unit_add_to_dbus_queue(UNIT(s));
- }
-
- return;
-
-fail:
- log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r));
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-
- if (cfd >= 0)
- close_nointr_nofail(cfd);
-
- dbus_error_free(&error);
-}
-
-static void socket_run_next(Socket *s) {
- int r;
-
- assert(s);
- assert(s->control_command);
- assert(s->control_command->command_next);
-
- socket_unwatch_control_pid(s);
-
- s->control_command = s->control_command->command_next;
-
- if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- return;
-
-fail:
- log_warning("%s failed to run next task: %s", UNIT(s)->id, strerror(-r));
-
- if (s->state == SOCKET_START_POST)
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- else if (s->state == SOCKET_STOP_POST)
- socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
- else
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
-}
-
-static int socket_start(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(s);
-
- /* We cannot fulfill this request right now, try again later
- * please! */
- if (s->state == SOCKET_STOP_PRE ||
- s->state == SOCKET_STOP_PRE_SIGKILL ||
- s->state == SOCKET_STOP_PRE_SIGTERM ||
- s->state == SOCKET_STOP_POST ||
- s->state == SOCKET_FINAL_SIGTERM ||
- s->state == SOCKET_FINAL_SIGKILL)
- return -EAGAIN;
-
- if (s->state == SOCKET_START_PRE ||
- s->state == SOCKET_START_POST)
- return 0;
-
- /* Cannot run this without the service being around */
- if (UNIT_DEREF(s->service)) {
- Service *service;
-
- service = SERVICE(UNIT_DEREF(s->service));
-
- if (UNIT(service)->load_state != UNIT_LOADED) {
- log_error("Socket service %s not loaded, refusing.", UNIT(service)->id);
- return -ENOENT;
- }
-
- /* If the service is already active we cannot start the
- * socket */
- if (service->state != SERVICE_DEAD &&
- service->state != SERVICE_FAILED &&
- service->state != SERVICE_AUTO_RESTART) {
- log_error("Socket service %s already active, refusing.", UNIT(service)->id);
- return -EBUSY;
- }
-
-#ifdef HAVE_SYSV_COMPAT
- if (service->sysv_path) {
- log_error("Using SysV services for socket activation is not supported. Refusing.");
- return -ENOENT;
- }
-#endif
- }
-
- assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
-
- s->result = SOCKET_SUCCESS;
- socket_enter_start_pre(s);
- return 0;
-}
-
-static int socket_stop(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(s);
-
- /* Already on it */
- if (s->state == SOCKET_STOP_PRE ||
- s->state == SOCKET_STOP_PRE_SIGTERM ||
- s->state == SOCKET_STOP_PRE_SIGKILL ||
- s->state == SOCKET_STOP_POST ||
- s->state == SOCKET_FINAL_SIGTERM ||
- s->state == SOCKET_FINAL_SIGKILL)
- return 0;
-
- /* If there's already something running we go directly into
- * kill mode. */
- if (s->state == SOCKET_START_PRE ||
- s->state == SOCKET_START_POST) {
- socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS);
- return -EAGAIN;
- }
-
- assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
-
- socket_enter_stop_pre(s, SOCKET_SUCCESS);
- return 0;
-}
-
-static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
- Socket *s = SOCKET(u);
- SocketPort *p;
- int r;
-
- assert(u);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
- unit_serialize_item(u, f, "result", socket_result_to_string(s->result));
- unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
-
- if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
-
- if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
-
- LIST_FOREACH(port, p, s->ports) {
- int copy;
-
- if (p->fd < 0)
- continue;
-
- if ((copy = fdset_put_dup(fds, p->fd)) < 0)
- return copy;
-
- if (p->type == SOCKET_SOCKET) {
- char *t;
-
- if ((r = socket_address_print(&p->address, &t)) < 0)
- return r;
-
- if (socket_address_family(&p->address) == AF_NETLINK)
- unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t);
- else
- unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t);
- free(t);
- } else if (p->type == SOCKET_SPECIAL)
- unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
- else {
- assert(p->type == SOCKET_FIFO);
- unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
- }
- }
-
- return 0;
-}
-
-static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Socket *s = SOCKET(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- SocketState state;
-
- if ((state = socket_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- s->deserialized_state = state;
- } else if (streq(key, "result")) {
- SocketResult f;
-
- f = socket_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != SOCKET_SUCCESS)
- s->result = f;
-
- } else if (streq(key, "n-accepted")) {
- unsigned k;
-
- if (safe_atou(value, &k) < 0)
- log_debug("Failed to parse n-accepted value %s", value);
- else
- s->n_accepted += k;
- } else if (streq(key, "control-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
- else
- s->control_pid = pid;
- } else if (streq(key, "control-command")) {
- SocketExecCommand id;
-
- if ((id = socket_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
- else {
- s->control_command_id = id;
- s->control_command = s->exec_command[id];
- }
- } else if (streq(key, "fifo")) {
- int fd, skip = 0;
- SocketPort *p;
-
- if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse fifo value %s", value);
- else {
-
- LIST_FOREACH(port, p, s->ports)
- if (p->type == SOCKET_FIFO &&
- streq_ptr(p->path, value+skip))
- break;
-
- if (p) {
- if (p->fd >= 0)
- close_nointr_nofail(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
-
- } else if (streq(key, "special")) {
- int fd, skip = 0;
- SocketPort *p;
-
- if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse special value %s", value);
- else {
-
- LIST_FOREACH(port, p, s->ports)
- if (p->type == SOCKET_SPECIAL &&
- streq_ptr(p->path, value+skip))
- break;
-
- if (p) {
- if (p->fd >= 0)
- close_nointr_nofail(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
-
- } else if (streq(key, "socket")) {
- int fd, type, skip = 0;
- SocketPort *p;
-
- if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket value %s", value);
- else {
-
- LIST_FOREACH(port, p, s->ports)
- if (socket_address_is(&p->address, value+skip, type))
- break;
-
- if (p) {
- if (p->fd >= 0)
- close_nointr_nofail(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
-
- } else if (streq(key, "netlink")) {
- int fd, skip = 0;
- SocketPort *p;
-
- if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket value %s", value);
- else {
-
- LIST_FOREACH(port, p, s->ports)
- if (socket_address_is_netlink(&p->address, value+skip))
- break;
-
- if (p) {
- if (p->fd >= 0)
- close_nointr_nofail(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState socket_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SOCKET(u)->state];
-}
-
-static const char *socket_sub_state_to_string(Unit *u) {
- assert(u);
-
- return socket_state_to_string(SOCKET(u)->state);
-}
-
-static bool socket_check_gc(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(u);
-
- return s->n_connections > 0;
-}
-
-static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
- Socket *s = SOCKET(u);
- int cfd = -1;
-
- assert(s);
- assert(fd >= 0);
-
- if (s->state != SOCKET_LISTENING)
- return;
-
- log_debug("Incoming traffic on %s", u->id);
-
- if (events != EPOLLIN) {
-
- if (events & EPOLLHUP)
- log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->id);
- else
- log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events);
-
- goto fail;
- }
-
- if (w->socket_accept) {
- for (;;) {
-
- if ((cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK)) < 0) {
-
- if (errno == EINTR)
- continue;
-
- log_error("Failed to accept socket: %m");
- goto fail;
- }
-
- break;
- }
-
- socket_apply_socket_options(s, cfd);
- }
-
- socket_enter_running(s, cfd);
- return;
-
-fail:
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
-}
-
-static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- Socket *s = SOCKET(u);
- SocketResult f;
-
- assert(s);
- assert(pid >= 0);
-
- if (pid != s->control_pid)
- return;
-
- s->control_pid = 0;
-
- if (is_clean_exit(code, status))
- f = SOCKET_SUCCESS;
- else if (code == CLD_EXITED)
- f = SOCKET_FAILURE_EXIT_CODE;
- else if (code == CLD_KILLED)
- f = SOCKET_FAILURE_SIGNAL;
- else if (code == CLD_DUMPED)
- f = SOCKET_FAILURE_CORE_DUMP;
- else
- assert_not_reached("Unknown code");
-
- if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
-
- if (s->control_command->ignore)
- f = SOCKET_SUCCESS;
- }
-
- log_full(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
-
- if (f != SOCKET_SUCCESS)
- s->result = f;
-
- if (s->control_command &&
- s->control_command->command_next &&
- f == SOCKET_SUCCESS) {
-
- log_debug("%s running next command for state %s", u->id, socket_state_to_string(s->state));
- socket_run_next(s);
- } else {
- s->control_command = NULL;
- s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
-
- /* No further commands for this step, so let's figure
- * out what to do next */
-
- log_debug("%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state));
-
- switch (s->state) {
-
- case SOCKET_START_PRE:
- if (f == SOCKET_SUCCESS)
- socket_enter_start_post(s);
- else
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f);
- break;
-
- case SOCKET_START_POST:
- if (f == SOCKET_SUCCESS)
- socket_enter_listening(s);
- else
- socket_enter_stop_pre(s, f);
- break;
-
- case SOCKET_STOP_PRE:
- case SOCKET_STOP_PRE_SIGTERM:
- case SOCKET_STOP_PRE_SIGKILL:
- socket_enter_stop_post(s, f);
- break;
-
- case SOCKET_STOP_POST:
- case SOCKET_FINAL_SIGTERM:
- case SOCKET_FINAL_SIGKILL:
- socket_enter_dead(s, f);
- break;
-
- default:
- assert_not_reached("Uh, control process died at wrong time.");
- }
- }
-
- /* Notify clients about changed exit status */
- unit_add_to_dbus_queue(u);
-}
-
-static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
- Socket *s = SOCKET(u);
-
- assert(s);
- assert(elapsed == 1);
- assert(w == &s->timer_watch);
-
- switch (s->state) {
-
- case SOCKET_START_PRE:
- log_warning("%s starting timed out. Terminating.", u->id);
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
- break;
-
- case SOCKET_START_POST:
- log_warning("%s starting timed out. Stopping.", u->id);
- socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
- break;
-
- case SOCKET_STOP_PRE:
- log_warning("%s stopping timed out. Terminating.", u->id);
- socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
- break;
-
- case SOCKET_STOP_PRE_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s stopping timed out. Killing.", u->id);
- socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
- } else {
- log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->id);
- socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
- }
- break;
-
- case SOCKET_STOP_PRE_SIGKILL:
- log_warning("%s still around after SIGKILL. Ignoring.", u->id);
- socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
- break;
-
- case SOCKET_STOP_POST:
- log_warning("%s stopping timed out (2). Terminating.", u->id);
- socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
- break;
-
- case SOCKET_FINAL_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s stopping timed out (2). Killing.", u->id);
- socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
- } else {
- log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->id);
- socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
- }
- break;
-
- case SOCKET_FINAL_SIGKILL:
- log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
- socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
- break;
-
- default:
- assert_not_reached("Timeout at wrong time.");
- }
-}
-
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
- int *rfds;
- unsigned rn_fds, k;
- SocketPort *p;
-
- assert(s);
- assert(fds);
- assert(n_fds);
-
- /* Called from the service code for requesting our fds */
-
- rn_fds = 0;
- LIST_FOREACH(port, p, s->ports)
- if (p->fd >= 0)
- rn_fds++;
-
- if (rn_fds <= 0) {
- *fds = NULL;
- *n_fds = 0;
- return 0;
- }
-
- if (!(rfds = new(int, rn_fds)))
- return -ENOMEM;
-
- k = 0;
- LIST_FOREACH(port, p, s->ports)
- if (p->fd >= 0)
- rfds[k++] = p->fd;
-
- assert(k == rn_fds);
-
- *fds = rfds;
- *n_fds = rn_fds;
-
- return 0;
-}
-
-void socket_notify_service_dead(Socket *s, bool failed_permanent) {
- assert(s);
-
- /* The service is dead. Dang!
- *
- * This is strictly for one-instance-for-all-connections
- * services. */
-
- if (s->state == SOCKET_RUNNING) {
- log_debug("%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent));
- if (failed_permanent)
- socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
- else
- socket_enter_listening(s);
- }
-}
-
-void socket_connection_unref(Socket *s) {
- assert(s);
-
- /* The service is dead. Yay!
- *
- * This is strictly for one-instance-per-connection
- * services. */
-
- assert(s->n_connections > 0);
- s->n_connections--;
-
- log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections);
-}
-
-static void socket_reset_failed(Unit *u) {
- Socket *s = SOCKET(u);
-
- assert(s);
-
- if (s->state == SOCKET_FAILED)
- socket_set_state(s, SOCKET_DEAD);
-
- s->result = SOCKET_SUCCESS;
-}
-
-static int socket_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
- Socket *s = SOCKET(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
- int q;
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
- r = q;
- goto finish;
- }
-
- if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
-}
-
-static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = "dead",
- [SOCKET_START_PRE] = "start-pre",
- [SOCKET_START_POST] = "start-post",
- [SOCKET_LISTENING] = "listening",
- [SOCKET_RUNNING] = "running",
- [SOCKET_STOP_PRE] = "stop-pre",
- [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
- [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
- [SOCKET_STOP_POST] = "stop-post",
- [SOCKET_FINAL_SIGTERM] = "final-sigterm",
- [SOCKET_FINAL_SIGKILL] = "final-sigkill",
- [SOCKET_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
-
-static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
- [SOCKET_EXEC_START_PRE] = "StartPre",
- [SOCKET_EXEC_START_POST] = "StartPost",
- [SOCKET_EXEC_STOP_PRE] = "StopPre",
- [SOCKET_EXEC_STOP_POST] = "StopPost"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
-
-static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
- [SOCKET_SUCCESS] = "success",
- [SOCKET_FAILURE_RESOURCES] = "resources",
- [SOCKET_FAILURE_TIMEOUT] = "timeout",
- [SOCKET_FAILURE_EXIT_CODE] = "exit-code",
- [SOCKET_FAILURE_SIGNAL] = "signal",
- [SOCKET_FAILURE_CORE_DUMP] = "core-dump",
- [SOCKET_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
-
-const UnitVTable socket_vtable = {
- .suffix = ".socket",
- .object_size = sizeof(Socket),
- .sections =
- "Unit\0"
- "Socket\0"
- "Install\0",
-
- .init = socket_init,
- .done = socket_done,
- .load = socket_load,
-
- .kill = socket_kill,
-
- .coldplug = socket_coldplug,
-
- .dump = socket_dump,
-
- .start = socket_start,
- .stop = socket_stop,
-
- .serialize = socket_serialize,
- .deserialize_item = socket_deserialize_item,
-
- .active_state = socket_active_state,
- .sub_state_to_string = socket_sub_state_to_string,
-
- .check_gc = socket_check_gc,
-
- .fd_event = socket_fd_event,
- .sigchld_event = socket_sigchld_event,
- .timer_event = socket_timer_event,
-
- .reset_failed = socket_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Socket",
- .bus_message_handler = bus_socket_message_handler,
- .bus_invalidating_properties = bus_socket_invalidating_properties
-};
diff --git a/src/socket.h b/src/socket.h
deleted file mode 100644
index 6470d8b..0000000
--- a/src/socket.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosockethfoo
-#define foosockethfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Socket Socket;
-
-#include "manager.h"
-#include "unit.h"
-#include "socket-util.h"
-#include "mount.h"
-#include "service.h"
-
-typedef enum SocketState {
- SOCKET_DEAD,
- SOCKET_START_PRE,
- SOCKET_START_POST,
- SOCKET_LISTENING,
- SOCKET_RUNNING,
- SOCKET_STOP_PRE,
- SOCKET_STOP_PRE_SIGTERM,
- SOCKET_STOP_PRE_SIGKILL,
- SOCKET_STOP_POST,
- SOCKET_FINAL_SIGTERM,
- SOCKET_FINAL_SIGKILL,
- SOCKET_FAILED,
- _SOCKET_STATE_MAX,
- _SOCKET_STATE_INVALID = -1
-} SocketState;
-
-typedef enum SocketExecCommand {
- SOCKET_EXEC_START_PRE,
- SOCKET_EXEC_START_POST,
- SOCKET_EXEC_STOP_PRE,
- SOCKET_EXEC_STOP_POST,
- _SOCKET_EXEC_COMMAND_MAX,
- _SOCKET_EXEC_COMMAND_INVALID = -1
-} SocketExecCommand;
-
-typedef enum SocketType {
- SOCKET_SOCKET,
- SOCKET_FIFO,
- SOCKET_SPECIAL,
- SOCKET_MQUEUE,
- _SOCKET_FIFO_MAX,
- _SOCKET_FIFO_INVALID = -1
-} SocketType;
-
-typedef enum SocketResult {
- SOCKET_SUCCESS,
- SOCKET_FAILURE_RESOURCES,
- SOCKET_FAILURE_TIMEOUT,
- SOCKET_FAILURE_EXIT_CODE,
- SOCKET_FAILURE_SIGNAL,
- SOCKET_FAILURE_CORE_DUMP,
- SOCKET_FAILURE_SERVICE_FAILED_PERMANENT,
- _SOCKET_RESULT_MAX,
- _SOCKET_RESULT_INVALID = -1
-} SocketResult;
-
-typedef struct SocketPort {
- SocketType type;
- int fd;
-
- SocketAddress address;
- char *path;
- Watch fd_watch;
-
- LIST_FIELDS(struct SocketPort, port);
-} SocketPort;
-
-struct Socket {
- Unit meta;
-
- LIST_HEAD(SocketPort, ports);
-
- unsigned n_accepted;
- unsigned n_connections;
- unsigned max_connections;
-
- unsigned backlog;
- usec_t timeout_usec;
-
- ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
- ExecContext exec_context;
-
- /* For Accept=no sockets refers to the one service we'll
- activate. For Accept=yes sockets is either NULL, or filled
- when the next service we spawn. */
- UnitRef service;
-
- SocketState state, deserialized_state;
-
- Watch timer_watch;
-
- ExecCommand* control_command;
- SocketExecCommand control_command_id;
- pid_t control_pid;
-
- mode_t directory_mode;
- mode_t socket_mode;
-
- SocketResult result;
-
- bool accept;
-
- /* Socket options */
- bool keep_alive;
- bool free_bind;
- bool transparent;
- bool broadcast;
- bool pass_cred;
- bool pass_sec;
- int priority;
- int mark;
- size_t receive_buffer;
- size_t send_buffer;
- int ip_tos;
- int ip_ttl;
- size_t pipe_size;
- char *bind_to_device;
- char *tcp_congestion;
- long mq_maxmsg;
- long mq_msgsize;
-
- /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
- SocketAddressBindIPv6Only bind_ipv6_only;
-};
-
-/* Called from the service code when collecting fds */
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
-
-/* Called from the service when it shut down */
-void socket_notify_service_dead(Socket *s, bool failed_permanent);
-
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the sockets of this socket */
-int socket_add_one_mount_link(Socket *s, Mount *m);
-
-/* Called from the service code when a per-connection service ended */
-void socket_connection_unref(Socket *s);
-
-extern const UnitVTable socket_vtable;
-
-const char* socket_state_to_string(SocketState i);
-SocketState socket_state_from_string(const char *s);
-
-const char* socket_exec_command_to_string(SocketExecCommand i);
-SocketExecCommand socket_exec_command_from_string(const char *s);
-
-const char* socket_result_to_string(SocketResult i);
-SocketResult socket_result_from_string(const char *s);
-
-#endif
diff --git a/src/spawn-agent.h b/src/spawn-agent.h
deleted file mode 100644
index fd0a910..0000000
--- a/src/spawn-agent.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foospawnagenthfoo
-#define foospawnagenthfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-void agent_open(void);
-void agent_close(void);
-
-#endif
diff --git a/src/special.h b/src/special.h
deleted file mode 100644
index 43e2e6f..0000000
--- a/src/special.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foospecialhfoo
-#define foospecialhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#define SPECIAL_DEFAULT_TARGET "default.target"
-
-/* Shutdown targets */
-#define SPECIAL_UMOUNT_TARGET "umount.target"
-/* This is not really intended to be started by directly. This is
- * mostly so that other targets (reboot/halt/poweroff) can depend on
- * it to bring all services down that want to be brought down on
- * system shutdown. */
-#define SPECIAL_SHUTDOWN_TARGET "shutdown.target"
-#define SPECIAL_HALT_TARGET "halt.target"
-#define SPECIAL_POWEROFF_TARGET "poweroff.target"
-#define SPECIAL_REBOOT_TARGET "reboot.target"
-#define SPECIAL_KEXEC_TARGET "kexec.target"
-#define SPECIAL_EXIT_TARGET "exit.target"
-
-/* Special boot targets */
-#define SPECIAL_RESCUE_TARGET "rescue.target"
-#define SPECIAL_EMERGENCY_TARGET "emergency.target"
-
-/* Early boot targets */
-#define SPECIAL_SYSINIT_TARGET "sysinit.target"
-#define SPECIAL_SOCKETS_TARGET "sockets.target"
-#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */
-#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
-#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
-#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
-#define SPECIAL_SWAP_TARGET "swap.target"
-#define SPECIAL_BASIC_TARGET "basic.target"
-
-/* LSB compatibility */
-#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
-#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
-#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
-#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */
-#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
-#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */
-#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */
-#define SPECIAL_HTTP_DAEMON_TARGET "http-daemon.target"
-
-/* Magic early boot services */
-#define SPECIAL_FSCK_SERVICE "fsck at .service"
-#define SPECIAL_QUOTACHECK_SERVICE "quotacheck.service"
-#define SPECIAL_QUOTAON_SERVICE "quotaon.service"
-#define SPECIAL_REMOUNT_ROOTFS_SERVICE "remount-rootfs.service"
-
-/* Services systemd relies on */
-#define SPECIAL_DBUS_SERVICE "dbus.service"
-#define SPECIAL_DBUS_SOCKET "dbus.socket"
-#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
-#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
-
-/* Magic init signals */
-#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
-#define SPECIAL_SIGPWR_TARGET "sigpwr.target"
-#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target"
-
-/* For SysV compatibility. Usually an alias for a saner target. On
- * SysV-free systems this doesn't exist. */
-#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target"
-#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
-#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
-#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
-
-#endif
diff --git a/src/swap.c b/src/swap.c
deleted file mode 100644
index 9c72732..0000000
--- a/src/swap.c
+++ /dev/null
@@ -1,1415 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <sys/stat.h>
-#include <sys/swap.h>
-#include <libudev.h>
-
-#include "unit.h"
-#include "swap.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "unit-name.h"
-#include "dbus-swap.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "exit-status.h"
-#include "def.h"
-
-static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
- [SWAP_DEAD] = UNIT_INACTIVE,
- [SWAP_ACTIVATING] = UNIT_ACTIVATING,
- [SWAP_ACTIVE] = UNIT_ACTIVE,
- [SWAP_DEACTIVATING] = UNIT_DEACTIVATING,
- [SWAP_ACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
- [SWAP_ACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
- [SWAP_DEACTIVATING_SIGTERM] = UNIT_DEACTIVATING,
- [SWAP_DEACTIVATING_SIGKILL] = UNIT_DEACTIVATING,
- [SWAP_FAILED] = UNIT_FAILED
-};
-
-static void swap_unset_proc_swaps(Swap *s) {
- Swap *first;
-
- assert(s);
-
- if (!s->parameters_proc_swaps.what)
- return;
-
- /* Remove this unit from the chain of swaps which share the
- * same kernel swap device. */
-
- first = hashmap_get(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
- LIST_REMOVE(Swap, same_proc_swaps, first, s);
-
- if (first)
- hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first);
- else
- hashmap_remove(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
-
- free(s->parameters_proc_swaps.what);
- s->parameters_proc_swaps.what = NULL;
-}
-
-static void swap_init(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
- assert(UNIT(s)->load_state == UNIT_STUB);
-
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
-
- exec_context_init(&s->exec_context);
- s->exec_context.std_output = u->manager->default_std_output;
- s->exec_context.std_error = u->manager->default_std_error;
-
- s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
-
- s->timer_watch.type = WATCH_INVALID;
-
- s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
-
- UNIT(s)->ignore_on_isolate = true;
-}
-
-static void swap_unwatch_control_pid(Swap *s) {
- assert(s);
-
- if (s->control_pid <= 0)
- return;
-
- unit_unwatch_pid(UNIT(s), s->control_pid);
- s->control_pid = 0;
-}
-
-static void swap_done(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
-
- swap_unset_proc_swaps(s);
-
- free(s->what);
- s->what = NULL;
-
- free(s->parameters_etc_fstab.what);
- free(s->parameters_fragment.what);
- s->parameters_etc_fstab.what = s->parameters_fragment.what = NULL;
-
- exec_context_done(&s->exec_context);
- exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
- s->control_command = NULL;
-
- swap_unwatch_control_pid(s);
-
- unit_unwatch_timer(u, &s->timer_watch);
-}
-
-int swap_add_one_mount_link(Swap *s, Mount *m) {
- int r;
-
- assert(s);
- assert(m);
-
- if (UNIT(s)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (is_device_path(s->what))
- return 0;
-
- if (!path_startswith(s->what, m->where))
- return 0;
-
- if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
-
- return 0;
-}
-
-static int swap_add_mount_links(Swap *s) {
- Unit *other;
- int r;
-
- assert(s);
-
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
- if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0)
- return r;
-
- return 0;
-}
-
-static int swap_add_target_links(Swap *s) {
- Unit *tu;
- SwapParameters *p;
- int r;
-
- assert(s);
-
- if (s->from_fragment)
- p = &s->parameters_fragment;
- else if (s->from_etc_fstab)
- p = &s->parameters_etc_fstab;
- else
- return 0;
-
- if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0)
- return r;
-
- if (!p->noauto &&
- !p->nofail &&
- (p->handle || UNIT(s)->manager->swap_auto) &&
- s->from_etc_fstab &&
- UNIT(s)->manager->running_as == MANAGER_SYSTEM)
- if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0)
- return r;
-
- return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true);
-}
-
-static int swap_add_device_links(Swap *s) {
- SwapParameters *p;
-
- assert(s);
-
- if (!s->what)
- return 0;
-
- if (s->from_fragment)
- p = &s->parameters_fragment;
- else if (s->from_etc_fstab)
- p = &s->parameters_etc_fstab;
- else
- return 0;
-
- if (is_device_path(s->what))
- return unit_add_node_link(UNIT(s), s->what,
- !p->noauto && p->nofail &&
- UNIT(s)->manager->running_as == MANAGER_SYSTEM);
- else
- /* File based swap devices need to be ordered after
- * remount-rootfs.service, since they might need a
- * writable file system. */
- return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_ROOTFS_SERVICE, NULL, true);
-}
-
-static int swap_add_default_dependencies(Swap *s) {
- int r;
-
- assert(s);
-
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int swap_verify(Swap *s) {
- bool b;
- char *e;
-
- if (UNIT(s)->load_state != UNIT_LOADED)
- return 0;
-
- if (!(e = unit_name_from_path(s->what, ".swap")))
- return -ENOMEM;
-
- b = unit_has_name(UNIT(s), e);
- free(e);
-
- if (!b) {
- log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int swap_load(Unit *u) {
- int r;
- Swap *s = SWAP(u);
-
- assert(s);
- assert(u->load_state == UNIT_STUB);
-
- /* Load a .swap file */
- if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_LOADED) {
- if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
- return r;
-
- if (UNIT(s)->fragment_path)
- s->from_fragment = true;
-
- if (!s->what) {
- if (s->parameters_fragment.what)
- s->what = strdup(s->parameters_fragment.what);
- else if (s->parameters_etc_fstab.what)
- s->what = strdup(s->parameters_etc_fstab.what);
- else if (s->parameters_proc_swaps.what)
- s->what = strdup(s->parameters_proc_swaps.what);
- else
- s->what = unit_name_to_path(u->id);
-
- if (!s->what)
- return -ENOMEM;
- }
-
- path_kill_slashes(s->what);
-
- if (!UNIT(s)->description)
- if ((r = unit_set_description(u, s->what)) < 0)
- return r;
-
- if ((r = swap_add_device_links(s)) < 0)
- return r;
-
- if ((r = swap_add_mount_links(s)) < 0)
- return r;
-
- if ((r = swap_add_target_links(s)) < 0)
- return r;
-
- if ((r = unit_add_default_cgroups(u)) < 0)
- return r;
-
- if (UNIT(s)->default_dependencies)
- if ((r = swap_add_default_dependencies(s)) < 0)
- return r;
- }
-
- return swap_verify(s);
-}
-
-int swap_add_one(
- Manager *m,
- const char *what,
- const char *what_proc_swaps,
- int priority,
- bool noauto,
- bool nofail,
- bool handle,
- bool set_flags) {
-
- Unit *u = NULL;
- char *e = NULL, *wp = NULL;
- bool delete = false;
- int r;
- SwapParameters *p;
-
- assert(m);
- assert(what);
-
- e = unit_name_from_path(what, ".swap");
- if (!e)
- return -ENOMEM;
-
- u = manager_get_unit(m, e);
-
- if (what_proc_swaps &&
- u &&
- SWAP(u)->from_proc_swaps &&
- !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps))
- return -EEXIST;
-
- if (!u) {
- delete = true;
-
- u = unit_new(m, sizeof(Swap));
- if (!u) {
- free(e);
- return -ENOMEM;
- }
-
- r = unit_add_name(u, e);
- if (r < 0)
- goto fail;
-
- SWAP(u)->what = strdup(what);
- if (!SWAP(u)->what) {
- r = -ENOMEM;
- goto fail;
- }
-
- unit_add_to_load_queue(u);
- } else
- delete = false;
-
- if (what_proc_swaps) {
- Swap *first;
-
- p = &SWAP(u)->parameters_proc_swaps;
-
- if (!p->what) {
- if (!(wp = strdup(what_proc_swaps))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!m->swaps_by_proc_swaps)
- if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- free(p->what);
- p->what = wp;
-
- first = hashmap_get(m->swaps_by_proc_swaps, wp);
- LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u));
-
- if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0)
- goto fail;
- }
-
- if (set_flags) {
- SWAP(u)->is_active = true;
- SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps;
- }
-
- SWAP(u)->from_proc_swaps = true;
-
- } else {
- p = &SWAP(u)->parameters_etc_fstab;
-
- if (!(wp = strdup(what))) {
- r = -ENOMEM;
- goto fail;
- }
-
- free(p->what);
- p->what = wp;
-
- SWAP(u)->from_etc_fstab = true;
- }
-
- p->priority = priority;
- p->noauto = noauto;
- p->nofail = nofail;
- p->handle = handle;
-
- unit_add_to_dbus_queue(u);
-
- free(e);
-
- return 0;
-
-fail:
- log_warning("Failed to load swap unit: %s", strerror(-r));
-
- free(wp);
- free(e);
-
- if (delete && u)
- unit_free(u);
-
- return r;
-}
-
-static int swap_process_new_swap(Manager *m, const char *device, int prio, bool set_flags) {
- struct stat st;
- int r = 0, k;
-
- assert(m);
-
- if (stat(device, &st) >= 0 && S_ISBLK(st.st_mode)) {
- struct udev_device *d;
- const char *dn;
- struct udev_list_entry *item = NULL, *first = NULL;
-
- /* So this is a proper swap device. Create swap units
- * for all names this swap device is known under */
-
- if (!(d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev)))
- return -ENOMEM;
-
- if ((dn = udev_device_get_devnode(d)))
- r = swap_add_one(m, dn, device, prio, false, false, false, set_flags);
-
- /* Add additional units for all symlinks */
- first = udev_device_get_devlinks_list_entry(d);
- udev_list_entry_foreach(item, first) {
- const char *p;
-
- /* Don't bother with the /dev/block links */
- p = udev_list_entry_get_name(item);
-
- if (path_startswith(p, "/dev/block/"))
- continue;
-
- if (stat(p, &st) >= 0)
- if ((!S_ISBLK(st.st_mode)) || st.st_rdev != udev_device_get_devnum(d))
- continue;
-
- if ((k = swap_add_one(m, p, device, prio, false, false, false, set_flags)) < 0)
- r = k;
- }
-
- udev_device_unref(d);
- }
-
- if ((k = swap_add_one(m, device, device, prio, false, false, false, set_flags)) < 0)
- r = k;
-
- return r;
-}
-
-static void swap_set_state(Swap *s, SwapState state) {
- SwapState old_state;
-
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != SWAP_ACTIVATING &&
- state != SWAP_ACTIVATING_SIGTERM &&
- state != SWAP_ACTIVATING_SIGKILL &&
- state != SWAP_DEACTIVATING &&
- state != SWAP_DEACTIVATING_SIGTERM &&
- state != SWAP_DEACTIVATING_SIGKILL) {
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
- swap_unwatch_control_pid(s);
- s->control_command = NULL;
- s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
- }
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(s)->id,
- swap_state_to_string(old_state),
- swap_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int swap_coldplug(Unit *u) {
- Swap *s = SWAP(u);
- SwapState new_state = SWAP_DEAD;
- int r;
-
- assert(s);
- assert(s->state == SWAP_DEAD);
-
- if (s->deserialized_state != s->state)
- new_state = s->deserialized_state;
- else if (s->from_proc_swaps)
- new_state = SWAP_ACTIVE;
-
- if (new_state != s->state) {
-
- if (new_state == SWAP_ACTIVATING ||
- new_state == SWAP_ACTIVATING_SIGTERM ||
- new_state == SWAP_ACTIVATING_SIGKILL ||
- new_state == SWAP_DEACTIVATING ||
- new_state == SWAP_DEACTIVATING_SIGTERM ||
- new_state == SWAP_DEACTIVATING_SIGKILL) {
-
- if (s->control_pid <= 0)
- return -EBADMSG;
-
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
- return r;
-
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- return r;
- }
-
- swap_set_state(s, new_state);
- }
-
- return 0;
-}
-
-static void swap_dump(Unit *u, FILE *f, const char *prefix) {
- Swap *s = SWAP(u);
- SwapParameters *p;
-
- assert(s);
- assert(f);
-
- if (s->from_proc_swaps)
- p = &s->parameters_proc_swaps;
- else if (s->from_fragment)
- p = &s->parameters_fragment;
- else
- p = &s->parameters_etc_fstab;
-
- fprintf(f,
- "%sSwap State: %s\n"
- "%sResult: %s\n"
- "%sWhat: %s\n"
- "%sPriority: %i\n"
- "%sNoAuto: %s\n"
- "%sNoFail: %s\n"
- "%sHandle: %s\n"
- "%sFrom /etc/fstab: %s\n"
- "%sFrom /proc/swaps: %s\n"
- "%sFrom fragment: %s\n",
- prefix, swap_state_to_string(s->state),
- prefix, swap_result_to_string(s->result),
- prefix, s->what,
- prefix, p->priority,
- prefix, yes_no(p->noauto),
- prefix, yes_no(p->nofail),
- prefix, yes_no(p->handle),
- prefix, yes_no(s->from_etc_fstab),
- prefix, yes_no(s->from_proc_swaps),
- prefix, yes_no(s->from_fragment));
-
- if (s->control_pid > 0)
- fprintf(f,
- "%sControl PID: %lu\n",
- prefix, (unsigned long) s->control_pid);
-
- exec_context_dump(&s->exec_context, f, prefix);
-}
-
-static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
- pid_t pid;
- int r;
-
- assert(s);
- assert(c);
- assert(_pid);
-
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
-
- if ((r = exec_spawn(c,
- NULL,
- &s->exec_context,
- NULL, 0,
- UNIT(s)->manager->environment,
- true,
- true,
- true,
- UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- &pid)) < 0)
- goto fail;
-
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
- /* FIXME: we need to do something here */
- goto fail;
-
- *_pid = pid;
-
- return 0;
-
-fail:
- unit_unwatch_timer(UNIT(s), &s->timer_watch);
-
- return r;
-}
-
-static void swap_enter_dead(Swap *s, SwapResult f) {
- assert(s);
-
- if (f != SWAP_SUCCESS)
- s->result = f;
-
- swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
-}
-
-static void swap_enter_active(Swap *s, SwapResult f) {
- assert(s);
-
- if (f != SWAP_SUCCESS)
- s->result = f;
-
- swap_set_state(s, SWAP_ACTIVE);
-}
-
-static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
- int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
-
- assert(s);
-
- if (f != SWAP_SUCCESS)
- s->result = f;
-
- if (s->exec_context.kill_mode != KILL_NONE) {
- int sig = (state == SWAP_ACTIVATING_SIGTERM ||
- state == SWAP_DEACTIVATING_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- if ((r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, pid_set)) < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning("Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
-
- if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
- goto fail;
-
- swap_set_state(s, state);
- } else
- swap_enter_dead(s, SWAP_SUCCESS);
-
- return;
-
-fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
-
- swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
-}
-
-static void swap_enter_activating(Swap *s) {
- int r, priority;
-
- assert(s);
-
- s->control_command_id = SWAP_EXEC_ACTIVATE;
- s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
-
- if (s->from_fragment)
- priority = s->parameters_fragment.priority;
- else if (s->from_etc_fstab)
- priority = s->parameters_etc_fstab.priority;
- else
- priority = -1;
-
- if (priority >= 0) {
- char p[LINE_MAX];
-
- snprintf(p, sizeof(p), "%i", priority);
- char_array_0(p);
-
- r = exec_command_set(
- s->control_command,
- "/sbin/swapon",
- "-p",
- p,
- s->what,
- NULL);
- } else
- r = exec_command_set(
- s->control_command,
- "/sbin/swapon",
- s->what,
- NULL);
-
- if (r < 0)
- goto fail;
-
- swap_unwatch_control_pid(s);
-
- if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- swap_set_state(s, SWAP_ACTIVATING);
-
- return;
-
-fail:
- log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r));
- swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
-}
-
-static void swap_enter_deactivating(Swap *s) {
- int r;
-
- assert(s);
-
- s->control_command_id = SWAP_EXEC_DEACTIVATE;
- s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE;
-
- if ((r = exec_command_set(
- s->control_command,
- "/sbin/swapoff",
- s->what,
- NULL)) < 0)
- goto fail;
-
- swap_unwatch_control_pid(s);
-
- if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
- goto fail;
-
- swap_set_state(s, SWAP_DEACTIVATING);
-
- return;
-
-fail:
- log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r));
- swap_enter_active(s, SWAP_FAILURE_RESOURCES);
-}
-
-static int swap_start(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
-
- /* We cannot fulfill this request right now, try again later
- * please! */
-
- if (s->state == SWAP_DEACTIVATING ||
- s->state == SWAP_DEACTIVATING_SIGTERM ||
- s->state == SWAP_DEACTIVATING_SIGKILL ||
- s->state == SWAP_ACTIVATING_SIGTERM ||
- s->state == SWAP_ACTIVATING_SIGKILL)
- return -EAGAIN;
-
- if (s->state == SWAP_ACTIVATING)
- return 0;
-
- assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED);
-
- s->result = SWAP_SUCCESS;
- swap_enter_activating(s);
- return 0;
-}
-
-static int swap_stop(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
-
- if (s->state == SWAP_DEACTIVATING ||
- s->state == SWAP_DEACTIVATING_SIGTERM ||
- s->state == SWAP_DEACTIVATING_SIGKILL ||
- s->state == SWAP_ACTIVATING_SIGTERM ||
- s->state == SWAP_ACTIVATING_SIGKILL)
- return 0;
-
- assert(s->state == SWAP_ACTIVATING ||
- s->state == SWAP_ACTIVE);
-
- swap_enter_deactivating(s);
- return 0;
-}
-
-static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
- Swap *s = SWAP(u);
-
- assert(s);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", swap_state_to_string(s->state));
- unit_serialize_item(u, f, "result", swap_result_to_string(s->result));
-
- if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
-
- if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
-
- return 0;
-}
-
-static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Swap *s = SWAP(u);
-
- assert(s);
- assert(fds);
-
- if (streq(key, "state")) {
- SwapState state;
-
- if ((state = swap_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- s->deserialized_state = state;
- } else if (streq(key, "result")) {
- SwapResult f;
-
- f = swap_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != SWAP_SUCCESS)
- s->result = f;
- } else if (streq(key, "control-pid")) {
- pid_t pid;
-
- if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
- else
- s->control_pid = pid;
-
- } else if (streq(key, "control-command")) {
- SwapExecCommand id;
-
- if ((id = swap_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
- else {
- s->control_command_id = id;
- s->control_command = s->exec_command + id;
- }
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState swap_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SWAP(u)->state];
-}
-
-static const char *swap_sub_state_to_string(Unit *u) {
- assert(u);
-
- return swap_state_to_string(SWAP(u)->state);
-}
-
-static bool swap_check_gc(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
-
- return s->from_etc_fstab || s->from_proc_swaps;
-}
-
-static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- Swap *s = SWAP(u);
- SwapResult f;
-
- assert(s);
- assert(pid >= 0);
-
- if (pid != s->control_pid)
- return;
-
- s->control_pid = 0;
-
- if (is_clean_exit(code, status))
- f = SWAP_SUCCESS;
- else if (code == CLD_EXITED)
- f = SWAP_FAILURE_EXIT_CODE;
- else if (code == CLD_KILLED)
- f = SWAP_FAILURE_SIGNAL;
- else if (code == CLD_DUMPED)
- f = SWAP_FAILURE_CORE_DUMP;
- else
- assert_not_reached("Unknown code");
-
- if (f != SWAP_SUCCESS)
- s->result = f;
-
- if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
-
- s->control_command = NULL;
- s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
- }
-
- log_full(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
-
- switch (s->state) {
-
- case SWAP_ACTIVATING:
- case SWAP_ACTIVATING_SIGTERM:
- case SWAP_ACTIVATING_SIGKILL:
-
- if (f == SWAP_SUCCESS)
- swap_enter_active(s, f);
- else
- swap_enter_dead(s, f);
- break;
-
- case SWAP_DEACTIVATING:
- case SWAP_DEACTIVATING_SIGKILL:
- case SWAP_DEACTIVATING_SIGTERM:
-
- if (f == SWAP_SUCCESS)
- swap_enter_dead(s, f);
- else
- swap_enter_dead(s, f);
- break;
-
- default:
- assert_not_reached("Uh, control process died at wrong time.");
- }
-
- /* Notify clients about changed exit status */
- unit_add_to_dbus_queue(u);
-
- /* Request a reload of /proc/swaps, so that following units
- * can follow our state change */
- u->manager->request_reload = true;
-}
-
-static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
- Swap *s = SWAP(u);
-
- assert(s);
- assert(elapsed == 1);
- assert(w == &s->timer_watch);
-
- switch (s->state) {
-
- case SWAP_ACTIVATING:
- log_warning("%s activation timed out. Stopping.", u->id);
- swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
- break;
-
- case SWAP_DEACTIVATING:
- log_warning("%s deactivation timed out. Stopping.", u->id);
- swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
- break;
-
- case SWAP_ACTIVATING_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s activation timed out. Killing.", u->id);
- swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
- } else {
- log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->id);
- swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
- }
- break;
-
- case SWAP_DEACTIVATING_SIGTERM:
- if (s->exec_context.send_sigkill) {
- log_warning("%s deactivation timed out. Killing.", u->id);
- swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
- } else {
- log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id);
- swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
- }
- break;
-
- case SWAP_ACTIVATING_SIGKILL:
- case SWAP_DEACTIVATING_SIGKILL:
- log_warning("%s swap process still around after SIGKILL. Ignoring.", u->id);
- swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
- break;
-
- default:
- assert_not_reached("Timeout at wrong time.");
- }
-}
-
-static int swap_load_proc_swaps(Manager *m, bool set_flags) {
- unsigned i;
- int r = 0;
-
- assert(m);
-
- rewind(m->proc_swaps);
-
- (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n");
-
- for (i = 1;; i++) {
- char *dev = NULL, *d;
- int prio = 0, k;
-
- if ((k = fscanf(m->proc_swaps,
- "%ms " /* device/file */
- "%*s " /* type of swap */
- "%*s " /* swap size */
- "%*s " /* used */
- "%i\n", /* priority */
- &dev, &prio)) != 2) {
-
- if (k == EOF)
- break;
-
- log_warning("Failed to parse /proc/swaps:%u.", i);
- free(dev);
- continue;
- }
-
- d = cunescape(dev);
- free(dev);
-
- if (!d)
- return -ENOMEM;
-
- k = swap_process_new_swap(m, d, prio, set_flags);
- free(d);
-
- if (k < 0)
- r = k;
- }
-
- return r;
-}
-
-int swap_dispatch_reload(Manager *m) {
- /* This function should go as soon as the kernel properly notifies us */
-
- if (_likely_(!m->request_reload))
- return 0;
-
- m->request_reload = false;
-
- return swap_fd_event(m, EPOLLPRI);
-}
-
-int swap_fd_event(Manager *m, int events) {
- Unit *u;
- int r;
-
- assert(m);
- assert(events & EPOLLPRI);
-
- if ((r = swap_load_proc_swaps(m, true)) < 0) {
- log_error("Failed to reread /proc/swaps: %s", strerror(-r));
-
- /* Reset flags, just in case, for late calls */
- LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) {
- Swap *swap = SWAP(u);
-
- swap->is_active = swap->just_activated = false;
- }
-
- return 0;
- }
-
- manager_dispatch_load_queue(m);
-
- LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) {
- Swap *swap = SWAP(u);
-
- if (!swap->is_active) {
- /* This has just been deactivated */
-
- swap->from_proc_swaps = false;
- swap_unset_proc_swaps(swap);
-
- switch (swap->state) {
-
- case SWAP_ACTIVE:
- swap_enter_dead(swap, SWAP_SUCCESS);
- break;
-
- default:
- swap_set_state(swap, swap->state);
- break;
- }
-
- } else if (swap->just_activated) {
-
- /* New swap entry */
-
- switch (swap->state) {
-
- case SWAP_DEAD:
- case SWAP_FAILED:
- swap_enter_active(swap, SWAP_SUCCESS);
- break;
-
- default:
- /* Nothing really changed, but let's
- * issue an notification call
- * nonetheless, in case somebody is
- * waiting for this. */
- swap_set_state(swap, swap->state);
- break;
- }
- }
-
- /* Reset the flags for later calls */
- swap->is_active = swap->just_activated = false;
- }
-
- return 1;
-}
-
-static Unit *swap_following(Unit *u) {
- Swap *s = SWAP(u);
- Swap *other, *first = NULL;
-
- assert(s);
-
- if (streq_ptr(s->what, s->parameters_proc_swaps.what))
- return NULL;
-
- /* Make everybody follow the unit that's named after the swap
- * device in the kernel */
-
- LIST_FOREACH_AFTER(same_proc_swaps, other, s)
- if (streq_ptr(other->what, other->parameters_proc_swaps.what))
- return UNIT(other);
-
- LIST_FOREACH_BEFORE(same_proc_swaps, other, s) {
- if (streq_ptr(other->what, other->parameters_proc_swaps.what))
- return UNIT(other);
-
- first = other;
- }
-
- return UNIT(first);
-}
-
-static int swap_following_set(Unit *u, Set **_set) {
- Swap *s = SWAP(u);
- Swap *other;
- Set *set;
- int r;
-
- assert(s);
- assert(_set);
-
- if (LIST_JUST_US(same_proc_swaps, s)) {
- *_set = NULL;
- return 0;
- }
-
- if (!(set = set_new(NULL, NULL)))
- return -ENOMEM;
-
- LIST_FOREACH_AFTER(same_proc_swaps, other, s)
- if ((r = set_put(set, other)) < 0)
- goto fail;
-
- LIST_FOREACH_BEFORE(same_proc_swaps, other, s)
- if ((r = set_put(set, other)) < 0)
- goto fail;
-
- *_set = set;
- return 1;
-
-fail:
- set_free(set);
- return r;
-}
-
-static void swap_shutdown(Manager *m) {
- assert(m);
-
- if (m->proc_swaps) {
- fclose(m->proc_swaps);
- m->proc_swaps = NULL;
- }
-
- hashmap_free(m->swaps_by_proc_swaps);
- m->swaps_by_proc_swaps = NULL;
-}
-
-static int swap_enumerate(Manager *m) {
- int r;
- struct epoll_event ev;
- assert(m);
-
- if (!m->proc_swaps) {
- if (!(m->proc_swaps = fopen("/proc/swaps", "re")))
- return (errno == ENOENT) ? 0 : -errno;
-
- m->swap_watch.type = WATCH_SWAP;
- m->swap_watch.fd = fileno(m->proc_swaps);
-
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->swap_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0)
- return -errno;
- }
-
- /* We rely on mount.c to load /etc/fstab for us */
-
- if ((r = swap_load_proc_swaps(m, false)) < 0)
- swap_shutdown(m);
-
- return r;
-}
-
-static void swap_reset_failed(Unit *u) {
- Swap *s = SWAP(u);
-
- assert(s);
-
- if (s->state == SWAP_FAILED)
- swap_set_state(s, SWAP_DEAD);
-
- s->result = SWAP_SUCCESS;
-}
-
-static int swap_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
- Swap *s = SWAP(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
- int q;
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
- r = q;
- goto finish;
- }
-
- if ((q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, pid_set)) < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
-}
-
-static const char* const swap_state_table[_SWAP_STATE_MAX] = {
- [SWAP_DEAD] = "dead",
- [SWAP_ACTIVATING] = "activating",
- [SWAP_ACTIVE] = "active",
- [SWAP_DEACTIVATING] = "deactivating",
- [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
- [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
- [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
- [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
- [SWAP_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
-
-static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
- [SWAP_EXEC_ACTIVATE] = "ExecActivate",
- [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand);
-
-static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
- [SWAP_SUCCESS] = "success",
- [SWAP_FAILURE_RESOURCES] = "resources",
- [SWAP_FAILURE_TIMEOUT] = "timeout",
- [SWAP_FAILURE_EXIT_CODE] = "exit-code",
- [SWAP_FAILURE_SIGNAL] = "signal",
- [SWAP_FAILURE_CORE_DUMP] = "core-dump"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
-
-const UnitVTable swap_vtable = {
- .suffix = ".swap",
- .object_size = sizeof(Swap),
- .sections =
- "Unit\0"
- "Swap\0"
- "Install\0",
-
- .no_alias = true,
- .no_instances = true,
- .show_status = true,
-
- .init = swap_init,
- .load = swap_load,
- .done = swap_done,
-
- .coldplug = swap_coldplug,
-
- .dump = swap_dump,
-
- .start = swap_start,
- .stop = swap_stop,
-
- .kill = swap_kill,
-
- .serialize = swap_serialize,
- .deserialize_item = swap_deserialize_item,
-
- .active_state = swap_active_state,
- .sub_state_to_string = swap_sub_state_to_string,
-
- .check_gc = swap_check_gc,
-
- .sigchld_event = swap_sigchld_event,
- .timer_event = swap_timer_event,
-
- .reset_failed = swap_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Swap",
- .bus_message_handler = bus_swap_message_handler,
- .bus_invalidating_properties = bus_swap_invalidating_properties,
-
- .following = swap_following,
- .following_set = swap_following_set,
-
- .enumerate = swap_enumerate,
- .shutdown = swap_shutdown
-};
diff --git a/src/swap.h b/src/swap.h
deleted file mode 100644
index 62d08da..0000000
--- a/src/swap.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooswaphfoo
-#define fooswaphfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2010 Maarten Lankhorst
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Swap Swap;
-
-#include "unit.h"
-
-typedef enum SwapState {
- SWAP_DEAD,
- SWAP_ACTIVATING,
- SWAP_ACTIVE,
- SWAP_DEACTIVATING,
- SWAP_ACTIVATING_SIGTERM,
- SWAP_ACTIVATING_SIGKILL,
- SWAP_DEACTIVATING_SIGTERM,
- SWAP_DEACTIVATING_SIGKILL,
- SWAP_FAILED,
- _SWAP_STATE_MAX,
- _SWAP_STATE_INVALID = -1
-} SwapState;
-
-typedef enum SwapExecCommand {
- SWAP_EXEC_ACTIVATE,
- SWAP_EXEC_DEACTIVATE,
- _SWAP_EXEC_COMMAND_MAX,
- _SWAP_EXEC_COMMAND_INVALID = -1
-} SwapExecCommand;
-
-typedef struct SwapParameters {
- char *what;
- int priority;
- bool noauto:1;
- bool nofail:1;
- bool handle:1;
-} SwapParameters;
-
-typedef enum SwapResult {
- SWAP_SUCCESS,
- SWAP_FAILURE_RESOURCES,
- SWAP_FAILURE_TIMEOUT,
- SWAP_FAILURE_EXIT_CODE,
- SWAP_FAILURE_SIGNAL,
- SWAP_FAILURE_CORE_DUMP,
- _SWAP_RESULT_MAX,
- _SWAP_RESULT_INVALID = -1
-} SwapResult;
-
-struct Swap {
- Unit meta;
-
- char *what;
-
- SwapParameters parameters_etc_fstab;
- SwapParameters parameters_proc_swaps;
- SwapParameters parameters_fragment;
-
- bool from_etc_fstab:1;
- bool from_proc_swaps:1;
- bool from_fragment:1;
-
- /* Used while looking for swaps that vanished or got added
- * from/to /proc/swaps */
- bool is_active:1;
- bool just_activated:1;
-
- SwapResult result;
-
- usec_t timeout_usec;
-
- ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX];
- ExecContext exec_context;
-
- SwapState state, deserialized_state;
-
- ExecCommand* control_command;
- SwapExecCommand control_command_id;
- pid_t control_pid;
-
- Watch timer_watch;
-
- /* In order to be able to distinguish dependencies on
- different device nodes we might end up creating multiple
- devices for the same swap. We chain them up here. */
-
- LIST_FIELDS(struct Swap, same_proc_swaps);
-};
-
-extern const UnitVTable swap_vtable;
-
-int swap_add_one(Manager *m, const char *what, const char *what_proc_swaps, int prio, bool no_auto, bool no_fail, bool handle, bool set_flags);
-
-int swap_add_one_mount_link(Swap *s, Mount *m);
-
-int swap_dispatch_reload(Manager *m);
-int swap_fd_event(Manager *m, int events);
-
-const char* swap_state_to_string(SwapState i);
-SwapState swap_state_from_string(const char *s);
-
-const char* swap_exec_command_to_string(SwapExecCommand i);
-SwapExecCommand swap_exec_command_from_string(const char *s);
-
-const char* swap_result_to_string(SwapResult i);
-SwapResult swap_result_from_string(const char *s);
-
-#endif
diff --git a/src/sysfs-show.h b/src/sysfs-show.h
deleted file mode 100644
index 9939e8b..0000000
--- a/src/sysfs-show.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosysfsshowhfoo
-#define foosysfsshowhfoo
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int show_sysfs(const char *seat, const char *prefix, unsigned columns);
-
-#endif
diff --git a/src/target.c b/src/target.c
deleted file mode 100644
index 6c1e0c3..0000000
--- a/src/target.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include "unit.h"
-#include "target.h"
-#include "load-fragment.h"
-#include "log.h"
-#include "dbus-target.h"
-#include "special.h"
-#include "unit-name.h"
-
-static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = UNIT_INACTIVE,
- [TARGET_ACTIVE] = UNIT_ACTIVE
-};
-
-static void target_set_state(Target *t, TargetState state) {
- TargetState old_state;
- assert(t);
-
- old_state = t->state;
- t->state = state;
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(t)->id,
- target_state_to_string(old_state),
- target_state_to_string(state));
-
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int target_add_default_dependencies(Target *t) {
- static const UnitDependency deps[] = {
- UNIT_REQUIRES,
- UNIT_REQUIRES_OVERRIDABLE,
- UNIT_REQUISITE,
- UNIT_REQUISITE_OVERRIDABLE,
- UNIT_WANTS,
- UNIT_BIND_TO
- };
-
- Iterator i;
- Unit *other;
- int r;
- unsigned k;
-
- assert(t);
-
- /* Imply ordering for requirement dependencies on target
- * units. Note that when the user created a contradicting
- * ordering manually we won't add anything in here to make
- * sure we don't create a loop. */
-
- for (k = 0; k < ELEMENTSOF(deps); k++)
- SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i)
- if ((r = unit_add_default_target_dependency(other, UNIT(t))) < 0)
- return r;
-
- /* Make sure targets are unloaded on shutdown */
- return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
-}
-
-static int target_load(Unit *u) {
- Target *t = TARGET(u);
- int r;
-
- assert(t);
-
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
- return r;
-
- /* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
- if (u->default_dependencies)
- if ((r = target_add_default_dependencies(t)) < 0)
- return r;
- }
-
- return 0;
-}
-
-static int target_coldplug(Unit *u) {
- Target *t = TARGET(u);
-
- assert(t);
- assert(t->state == TARGET_DEAD);
-
- if (t->deserialized_state != t->state)
- target_set_state(t, t->deserialized_state);
-
- return 0;
-}
-
-static void target_dump(Unit *u, FILE *f, const char *prefix) {
- Target *t = TARGET(u);
-
- assert(t);
- assert(f);
-
- fprintf(f,
- "%sTarget State: %s\n",
- prefix, target_state_to_string(t->state));
-}
-
-static int target_start(Unit *u) {
- Target *t = TARGET(u);
-
- assert(t);
- assert(t->state == TARGET_DEAD);
-
- target_set_state(t, TARGET_ACTIVE);
- return 0;
-}
-
-static int target_stop(Unit *u) {
- Target *t = TARGET(u);
-
- assert(t);
- assert(t->state == TARGET_ACTIVE);
-
- target_set_state(t, TARGET_DEAD);
- return 0;
-}
-
-static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
- Target *s = TARGET(u);
-
- assert(s);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", target_state_to_string(s->state));
- return 0;
-}
-
-static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Target *s = TARGET(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- TargetState state;
-
- if ((state = target_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- s->deserialized_state = state;
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState target_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[TARGET(u)->state];
-}
-
-static const char *target_sub_state_to_string(Unit *u) {
- assert(u);
-
- return target_state_to_string(TARGET(u)->state);
-}
-
-static const char* const target_state_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
-
-const UnitVTable target_vtable = {
- .suffix = ".target",
- .object_size = sizeof(Target),
- .sections =
- "Unit\0"
- "Target\0"
- "Install\0",
-
- .load = target_load,
- .coldplug = target_coldplug,
-
- .dump = target_dump,
-
- .start = target_start,
- .stop = target_stop,
-
- .serialize = target_serialize,
- .deserialize_item = target_deserialize_item,
-
- .active_state = target_active_state,
- .sub_state_to_string = target_sub_state_to_string,
-
- .bus_interface = "org.freedesktop.systemd1.Target",
- .bus_message_handler = bus_target_message_handler
-};
diff --git a/src/target.h b/src/target.h
deleted file mode 100644
index 5b97c86..0000000
--- a/src/target.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef footargethfoo
-#define footargethfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Target Target;
-
-#include "unit.h"
-
-typedef enum TargetState {
- TARGET_DEAD,
- TARGET_ACTIVE,
- _TARGET_STATE_MAX,
- _TARGET_STATE_INVALID = -1
-} TargetState;
-
-struct Target {
- Unit meta;
-
- TargetState state, deserialized_state;
-};
-
-extern const UnitVTable target_vtable;
-
-const char* target_state_to_string(TargetState i);
-TargetState target_state_from_string(const char *s);
-
-#endif
diff --git a/src/tcpwrap.c b/src/tcpwrap.c
deleted file mode 100644
index 0aab142..0000000
--- a/src/tcpwrap.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#ifdef HAVE_LIBWRAP
-#include <tcpd.h>
-#endif
-
-#include "tcpwrap.h"
-#include "log.h"
-
-bool socket_tcpwrap(int fd, const char *name) {
-#ifdef HAVE_LIBWRAP
- struct request_info req;
- union {
- struct sockaddr sa;
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- struct sockaddr_un un;
- struct sockaddr_storage storage;
- } sa_union;
- socklen_t l = sizeof(sa_union);
-
- if (getsockname(fd, &sa_union.sa, &l) < 0)
- return true;
-
- if (sa_union.sa.sa_family != AF_INET &&
- sa_union.sa.sa_family != AF_INET6)
- return true;
-
- request_init(&req,
- RQ_DAEMON, name,
- RQ_FILE, fd,
- NULL);
-
- fromhost(&req);
-
- if (!hosts_access(&req)) {
- log_warning("Connection refused by tcpwrap.");
- return false;
- }
-
- log_debug("Connection accepted by tcpwrap.");
-#endif
- return true;
-}
diff --git a/src/tcpwrap.h b/src/tcpwrap.h
deleted file mode 100644
index 4d4553e..0000000
--- a/src/tcpwrap.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foolibwraphfoo
-#define foolibwraphfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-bool socket_tcpwrap(int fd, const char *name);
-
-#endif
diff --git a/src/timer.c b/src/timer.c
deleted file mode 100644
index e318fee..0000000
--- a/src/timer.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-
-#include "unit.h"
-#include "unit-name.h"
-#include "timer.h"
-#include "dbus-timer.h"
-#include "special.h"
-#include "bus-errors.h"
-
-static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
- [TIMER_DEAD] = UNIT_INACTIVE,
- [TIMER_WAITING] = UNIT_ACTIVE,
- [TIMER_RUNNING] = UNIT_ACTIVE,
- [TIMER_ELAPSED] = UNIT_ACTIVE,
- [TIMER_FAILED] = UNIT_FAILED
-};
-
-static void timer_init(Unit *u) {
- Timer *t = TIMER(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- t->next_elapse = (usec_t) -1;
-}
-
-static void timer_done(Unit *u) {
- Timer *t = TIMER(u);
- TimerValue *v;
-
- assert(t);
-
- while ((v = t->values)) {
- LIST_REMOVE(TimerValue, value, t->values, v);
- free(v);
- }
-
- unit_unwatch_timer(u, &t->timer_watch);
-
- unit_ref_unset(&t->unit);
-}
-
-static int timer_verify(Timer *t) {
- assert(t);
-
- if (UNIT(t)->load_state != UNIT_LOADED)
- return 0;
-
- if (!t->values) {
- log_error("%s lacks value setting. Refusing.", UNIT(t)->id);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int timer_add_default_dependencies(Timer *t) {
- int r;
-
- assert(t);
-
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
- return r;
- }
-
- return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
-}
-
-static int timer_load(Unit *u) {
- Timer *t = TIMER(u);
- int r;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_LOADED) {
-
- if (!UNIT_DEREF(t->unit)) {
- Unit *x;
-
- r = unit_load_related_unit(u, ".service", &x);
- if (r < 0)
- return r;
-
- unit_ref_set(&t->unit, x);
- }
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
- if (r < 0)
- return r;
-
- if (UNIT(t)->default_dependencies)
- if ((r = timer_add_default_dependencies(t)) < 0)
- return r;
- }
-
- return timer_verify(t);
-}
-
-static void timer_dump(Unit *u, FILE *f, const char *prefix) {
- Timer *t = TIMER(u);
- TimerValue *v;
- char
- timespan1[FORMAT_TIMESPAN_MAX];
-
- fprintf(f,
- "%sTimer State: %s\n"
- "%sResult: %s\n"
- "%sUnit: %s\n",
- prefix, timer_state_to_string(t->state),
- prefix, timer_result_to_string(t->result),
- prefix, UNIT_DEREF(t->unit)->id);
-
- LIST_FOREACH(value, v, t->values)
- fprintf(f,
- "%s%s: %s\n",
- prefix,
- timer_base_to_string(v->base),
- strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
-}
-
-static void timer_set_state(Timer *t, TimerState state) {
- TimerState old_state;
- assert(t);
-
- old_state = t->state;
- t->state = state;
-
- if (state != TIMER_WAITING)
- unit_unwatch_timer(UNIT(t), &t->timer_watch);
-
- if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(t)->id,
- timer_state_to_string(old_state),
- timer_state_to_string(state));
-
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static void timer_enter_waiting(Timer *t, bool initial);
-
-static int timer_coldplug(Unit *u) {
- Timer *t = TIMER(u);
-
- assert(t);
- assert(t->state == TIMER_DEAD);
-
- if (t->deserialized_state != t->state) {
-
- if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
- else
- timer_set_state(t, t->deserialized_state);
- }
-
- return 0;
-}
-
-static void timer_enter_dead(Timer *t, TimerResult f) {
- assert(t);
-
- if (f != TIMER_SUCCESS)
- t->result = f;
-
- timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
-}
-
-static void timer_enter_waiting(Timer *t, bool initial) {
- TimerValue *v;
- usec_t base = 0, delay, n;
- bool found = false;
- int r;
-
- n = now(CLOCK_MONOTONIC);
-
- LIST_FOREACH(value, v, t->values) {
-
- if (v->disabled)
- continue;
-
- switch (v->base) {
-
- case TIMER_ACTIVE:
- if (state_translation_table[t->state] == UNIT_ACTIVE)
- base = UNIT(t)->inactive_exit_timestamp.monotonic;
- else
- base = n;
- break;
-
- case TIMER_BOOT:
- /* CLOCK_MONOTONIC equals the uptime on Linux */
- base = 0;
- break;
-
- case TIMER_STARTUP:
- base = UNIT(t)->manager->startup_timestamp.monotonic;
- break;
-
- case TIMER_UNIT_ACTIVE:
-
- if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
- continue;
-
- base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
- break;
-
- case TIMER_UNIT_INACTIVE:
-
- if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
- continue;
-
- base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
- break;
-
- default:
- assert_not_reached("Unknown timer base");
- }
-
- v->next_elapse = base + v->value;
-
- if (!initial && v->next_elapse < n) {
- v->disabled = true;
- continue;
- }
-
- if (!found)
- t->next_elapse = v->next_elapse;
- else
- t->next_elapse = MIN(t->next_elapse, v->next_elapse);
-
- found = true;
- }
-
- if (!found) {
- timer_set_state(t, TIMER_ELAPSED);
- return;
- }
-
- delay = n < t->next_elapse ? t->next_elapse - n : 0;
-
- if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0)
- goto fail;
-
- timer_set_state(t, TIMER_WAITING);
- return;
-
-fail:
- log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
- timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
-}
-
-static void timer_enter_running(Timer *t) {
- DBusError error;
- int r;
-
- assert(t);
- dbus_error_init(&error);
-
- /* Don't start job if we are supposed to go down */
- if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
- return;
-
- if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0)
- goto fail;
-
- timer_set_state(t, TIMER_RUNNING);
- return;
-
-fail:
- log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
- timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
-
- dbus_error_free(&error);
-}
-
-static int timer_start(Unit *u) {
- Timer *t = TIMER(u);
-
- assert(t);
- assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
-
- if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
- return -ENOENT;
-
- t->result = TIMER_SUCCESS;
- timer_enter_waiting(t, true);
- return 0;
-}
-
-static int timer_stop(Unit *u) {
- Timer *t = TIMER(u);
-
- assert(t);
- assert(t->state == TIMER_WAITING || t->state == TIMER_RUNNING || t->state == TIMER_ELAPSED);
-
- timer_enter_dead(t, TIMER_SUCCESS);
- return 0;
-}
-
-static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
- Timer *t = TIMER(u);
-
- assert(u);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", timer_state_to_string(t->state));
- unit_serialize_item(u, f, "result", timer_result_to_string(t->result));
-
- return 0;
-}
-
-static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Timer *t = TIMER(u);
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- TimerState state;
-
- if ((state = timer_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
- else
- t->deserialized_state = state;
- } else if (streq(key, "result")) {
- TimerResult f;
-
- f = timer_result_from_string(value);
- if (f < 0)
- log_debug("Failed to parse result value %s", value);
- else if (f != TIMER_SUCCESS)
- t->result = f;
-
- } else
- log_debug("Unknown serialization key '%s'", key);
-
- return 0;
-}
-
-static UnitActiveState timer_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[TIMER(u)->state];
-}
-
-static const char *timer_sub_state_to_string(Unit *u) {
- assert(u);
-
- return timer_state_to_string(TIMER(u)->state);
-}
-
-static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
- Timer *t = TIMER(u);
-
- assert(t);
- assert(elapsed == 1);
-
- if (t->state != TIMER_WAITING)
- return;
-
- log_debug("Timer elapsed on %s", u->id);
- timer_enter_running(t);
-}
-
-void timer_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_TIMER)
- return;
-
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Timer *t;
- TimerValue *v;
-
- if (k->type != UNIT_TIMER)
- continue;
-
- if (k->load_state != UNIT_LOADED)
- continue;
-
- t = TIMER(k);
-
- /* Reenable all timers that depend on unit state */
- LIST_FOREACH(value, v, t->values)
- if (v->base == TIMER_UNIT_ACTIVE ||
- v->base == TIMER_UNIT_INACTIVE)
- v->disabled = false;
-
- switch (t->state) {
-
- case TIMER_WAITING:
- case TIMER_ELAPSED:
-
- /* Recalculate sleep time */
- timer_enter_waiting(t, false);
- break;
-
- case TIMER_RUNNING:
-
- if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
- log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
- timer_enter_waiting(t, false);
- }
-
- break;
-
- case TIMER_DEAD:
- case TIMER_FAILED:
- break;
-
- default:
- assert_not_reached("Unknown timer state");
- }
- }
-}
-
-static void timer_reset_failed(Unit *u) {
- Timer *t = TIMER(u);
-
- assert(t);
-
- if (t->state == TIMER_FAILED)
- timer_set_state(t, TIMER_DEAD);
-
- t->result = TIMER_SUCCESS;
-}
-
-static const char* const timer_state_table[_TIMER_STATE_MAX] = {
- [TIMER_DEAD] = "dead",
- [TIMER_WAITING] = "waiting",
- [TIMER_RUNNING] = "running",
- [TIMER_ELAPSED] = "elapsed",
- [TIMER_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
-
-static const char* const timer_base_table[_TIMER_BASE_MAX] = {
- [TIMER_ACTIVE] = "OnActiveSec",
- [TIMER_BOOT] = "OnBootSec",
- [TIMER_STARTUP] = "OnStartupSec",
- [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
- [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
-
-static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
- [TIMER_SUCCESS] = "success",
- [TIMER_FAILURE_RESOURCES] = "resources"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
-
-const UnitVTable timer_vtable = {
- .suffix = ".timer",
- .object_size = sizeof(Timer),
- .sections =
- "Unit\0"
- "Timer\0"
- "Install\0",
-
- .init = timer_init,
- .done = timer_done,
- .load = timer_load,
-
- .coldplug = timer_coldplug,
-
- .dump = timer_dump,
-
- .start = timer_start,
- .stop = timer_stop,
-
- .serialize = timer_serialize,
- .deserialize_item = timer_deserialize_item,
-
- .active_state = timer_active_state,
- .sub_state_to_string = timer_sub_state_to_string,
-
- .timer_event = timer_timer_event,
-
- .reset_failed = timer_reset_failed,
-
- .bus_interface = "org.freedesktop.systemd1.Timer",
- .bus_message_handler = bus_timer_message_handler,
- .bus_invalidating_properties = bus_timer_invalidating_properties
-};
diff --git a/src/timer.h b/src/timer.h
deleted file mode 100644
index f5c5c64..0000000
--- a/src/timer.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef footimerhfoo
-#define footimerhfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct Timer Timer;
-
-#include "unit.h"
-
-typedef enum TimerState {
- TIMER_DEAD,
- TIMER_WAITING,
- TIMER_RUNNING,
- TIMER_ELAPSED,
- TIMER_FAILED,
- _TIMER_STATE_MAX,
- _TIMER_STATE_INVALID = -1
-} TimerState;
-
-typedef enum TimerBase {
- TIMER_ACTIVE,
- TIMER_BOOT,
- TIMER_STARTUP,
- TIMER_UNIT_ACTIVE,
- TIMER_UNIT_INACTIVE,
- _TIMER_BASE_MAX,
- _TIMER_BASE_INVALID = -1
-} TimerBase;
-
-typedef struct TimerValue {
- usec_t value;
- usec_t next_elapse;
-
- LIST_FIELDS(struct TimerValue, value);
-
- TimerBase base;
- bool disabled;
-} TimerValue;
-
-typedef enum TimerResult {
- TIMER_SUCCESS,
- TIMER_FAILURE_RESOURCES,
- _TIMER_RESULT_MAX,
- _TIMER_RESULT_INVALID = -1
-} TimerResult;
-
-struct Timer {
- Unit meta;
-
- LIST_HEAD(TimerValue, values);
- usec_t next_elapse;
-
- TimerState state, deserialized_state;
- UnitRef unit;
-
- Watch timer_watch;
-
- TimerResult result;
-};
-
-void timer_unit_notify(Unit *u, UnitActiveState new_state);
-
-extern const UnitVTable timer_vtable;
-
-const char *timer_state_to_string(TimerState i);
-TimerState timer_state_from_string(const char *s);
-
-const char *timer_base_to_string(TimerBase i);
-TimerBase timer_base_from_string(const char *s);
-
-const char* timer_result_to_string(TimerResult i);
-TimerResult timer_result_from_string(const char *s);
-
-#endif
diff --git a/src/unit.c b/src/unit.c
deleted file mode 100644
index 9e33701..0000000
--- a/src/unit.c
+++ /dev/null
@@ -1,2676 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <sys/poll.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "unit-name.h"
-#include "specifier.h"
-#include "dbus-unit.h"
-#include "special.h"
-#include "cgroup-util.h"
-#include "missing.h"
-#include "cgroup-attr.h"
-
-const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
- [UNIT_SERVICE] = &service_vtable,
- [UNIT_TIMER] = &timer_vtable,
- [UNIT_SOCKET] = &socket_vtable,
- [UNIT_TARGET] = &target_vtable,
- [UNIT_DEVICE] = &device_vtable,
- [UNIT_MOUNT] = &mount_vtable,
- [UNIT_AUTOMOUNT] = &automount_vtable,
- [UNIT_SNAPSHOT] = &snapshot_vtable,
- [UNIT_SWAP] = &swap_vtable,
- [UNIT_PATH] = &path_vtable
-};
-
-Unit *unit_new(Manager *m, size_t size) {
- Unit *u;
-
- assert(m);
- assert(size >= sizeof(Unit));
-
- u = malloc0(size);
- if (!u)
- return NULL;
-
- u->names = set_new(string_hash_func, string_compare_func);
- if (!u->names) {
- free(u);
- return NULL;
- }
-
- u->manager = m;
- u->type = _UNIT_TYPE_INVALID;
- u->deserialized_job = _JOB_TYPE_INVALID;
- u->default_dependencies = true;
- u->unit_file_state = _UNIT_FILE_STATE_INVALID;
-
- return u;
-}
-
-bool unit_has_name(Unit *u, const char *name) {
- assert(u);
- assert(name);
-
- return !!set_get(u->names, (char*) name);
-}
-
-int unit_add_name(Unit *u, const char *text) {
- UnitType t;
- char *s, *i = NULL;
- int r;
-
- assert(u);
- assert(text);
-
- if (unit_name_is_template(text)) {
- if (!u->instance)
- return -EINVAL;
-
- s = unit_name_replace_instance(text, u->instance);
- } else
- s = strdup(text);
-
- if (!s)
- return -ENOMEM;
-
- if (!unit_name_is_valid(s, false)) {
- r = -EINVAL;
- goto fail;
- }
-
- assert_se((t = unit_name_to_type(s)) >= 0);
-
- if (u->type != _UNIT_TYPE_INVALID && t != u->type) {
- r = -EINVAL;
- goto fail;
- }
-
- if ((r = unit_name_to_instance(s, &i)) < 0)
- goto fail;
-
- if (i && unit_vtable[t]->no_instances) {
- r = -EINVAL;
- goto fail;
- }
-
- /* Ensure that this unit is either instanced or not instanced,
- * but not both. */
- if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) {
- r = -EINVAL;
- goto fail;
- }
-
- if (unit_vtable[t]->no_alias &&
- !set_isempty(u->names) &&
- !set_get(u->names, s)) {
- r = -EEXIST;
- goto fail;
- }
-
- if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) {
- r = -E2BIG;
- goto fail;
- }
-
- if ((r = set_put(u->names, s)) < 0) {
- if (r == -EEXIST)
- r = 0;
- goto fail;
- }
-
- if ((r = hashmap_put(u->manager->units, s, u)) < 0) {
- set_remove(u->names, s);
- goto fail;
- }
-
- if (u->type == _UNIT_TYPE_INVALID) {
-
- u->type = t;
- u->id = s;
- u->instance = i;
-
- LIST_PREPEND(Unit, units_by_type, u->manager->units_by_type[t], u);
-
- if (UNIT_VTABLE(u)->init)
- UNIT_VTABLE(u)->init(u);
- } else
- free(i);
-
- unit_add_to_dbus_queue(u);
- return 0;
-
-fail:
- free(s);
- free(i);
-
- return r;
-}
-
-int unit_choose_id(Unit *u, const char *name) {
- char *s, *t = NULL, *i;
- int r;
-
- assert(u);
- assert(name);
-
- if (unit_name_is_template(name)) {
-
- if (!u->instance)
- return -EINVAL;
-
- if (!(t = unit_name_replace_instance(name, u->instance)))
- return -ENOMEM;
-
- name = t;
- }
-
- /* Selects one of the names of this unit as the id */
- s = set_get(u->names, (char*) name);
- free(t);
-
- if (!s)
- return -ENOENT;
-
- if ((r = unit_name_to_instance(s, &i)) < 0)
- return r;
-
- u->id = s;
-
- free(u->instance);
- u->instance = i;
-
- unit_add_to_dbus_queue(u);
-
- return 0;
-}
-
-int unit_set_description(Unit *u, const char *description) {
- char *s;
-
- assert(u);
-
- if (!(s = strdup(description)))
- return -ENOMEM;
-
- free(u->description);
- u->description = s;
-
- unit_add_to_dbus_queue(u);
- return 0;
-}
-
-bool unit_check_gc(Unit *u) {
- assert(u);
-
- if (u->load_state == UNIT_STUB)
- return true;
-
- if (UNIT_VTABLE(u)->no_gc)
- return true;
-
- if (u->no_gc)
- return true;
-
- if (u->job)
- return true;
-
- if (unit_active_state(u) != UNIT_INACTIVE)
- return true;
-
- if (UNIT_VTABLE(u)->check_gc)
- if (UNIT_VTABLE(u)->check_gc(u))
- return true;
-
- return false;
-}
-
-void unit_add_to_load_queue(Unit *u) {
- assert(u);
- assert(u->type != _UNIT_TYPE_INVALID);
-
- if (u->load_state != UNIT_STUB || u->in_load_queue)
- return;
-
- LIST_PREPEND(Unit, load_queue, u->manager->load_queue, u);
- u->in_load_queue = true;
-}
-
-void unit_add_to_cleanup_queue(Unit *u) {
- assert(u);
-
- if (u->in_cleanup_queue)
- return;
-
- LIST_PREPEND(Unit, cleanup_queue, u->manager->cleanup_queue, u);
- u->in_cleanup_queue = true;
-}
-
-void unit_add_to_gc_queue(Unit *u) {
- assert(u);
-
- if (u->in_gc_queue || u->in_cleanup_queue)
- return;
-
- if (unit_check_gc(u))
- return;
-
- LIST_PREPEND(Unit, gc_queue, u->manager->gc_queue, u);
- u->in_gc_queue = true;
-
- u->manager->n_in_gc_queue ++;
-
- if (u->manager->gc_queue_timestamp <= 0)
- u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC);
-}
-
-void unit_add_to_dbus_queue(Unit *u) {
- assert(u);
- assert(u->type != _UNIT_TYPE_INVALID);
-
- if (u->load_state == UNIT_STUB || u->in_dbus_queue)
- return;
-
- /* Shortcut things if nobody cares */
- if (!bus_has_subscriber(u->manager)) {
- u->sent_dbus_new_signal = true;
- return;
- }
-
- LIST_PREPEND(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
- u->in_dbus_queue = true;
-}
-
-static void bidi_set_free(Unit *u, Set *s) {
- Iterator i;
- Unit *other;
-
- assert(u);
-
- /* Frees the set and makes sure we are dropped from the
- * inverse pointers */
-
- SET_FOREACH(other, s, i) {
- UnitDependency d;
-
- for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
- set_remove(other->dependencies[d], u);
-
- unit_add_to_gc_queue(other);
- }
-
- set_free(s);
-}
-
-void unit_free(Unit *u) {
- UnitDependency d;
- Iterator i;
- char *t;
-
- assert(u);
-
- bus_unit_send_removed_signal(u);
-
- if (u->load_state != UNIT_STUB)
- if (UNIT_VTABLE(u)->done)
- UNIT_VTABLE(u)->done(u);
-
- SET_FOREACH(t, u->names, i)
- hashmap_remove_value(u->manager->units, t, u);
-
- if (u->job)
- job_free(u->job);
-
- for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
- bidi_set_free(u, u->dependencies[d]);
-
- if (u->type != _UNIT_TYPE_INVALID)
- LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
-
- if (u->in_load_queue)
- LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
-
- if (u->in_dbus_queue)
- LIST_REMOVE(Unit, dbus_queue, u->manager->dbus_unit_queue, u);
-
- if (u->in_cleanup_queue)
- LIST_REMOVE(Unit, cleanup_queue, u->manager->cleanup_queue, u);
-
- if (u->in_gc_queue) {
- LIST_REMOVE(Unit, gc_queue, u->manager->gc_queue, u);
- u->manager->n_in_gc_queue--;
- }
-
- cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0);
- cgroup_attribute_free_list(u->cgroup_attributes);
-
- free(u->description);
- free(u->fragment_path);
- free(u->instance);
-
- set_free_free(u->names);
-
- condition_free_list(u->conditions);
-
- while (u->refs)
- unit_ref_unset(u->refs);
-
- free(u);
-}
-
-UnitActiveState unit_active_state(Unit *u) {
- assert(u);
-
- if (u->load_state == UNIT_MERGED)
- return unit_active_state(unit_follow_merge(u));
-
- /* After a reload it might happen that a unit is not correctly
- * loaded but still has a process around. That's why we won't
- * shortcut failed loading to UNIT_INACTIVE_FAILED. */
-
- return UNIT_VTABLE(u)->active_state(u);
-}
-
-const char* unit_sub_state_to_string(Unit *u) {
- assert(u);
-
- return UNIT_VTABLE(u)->sub_state_to_string(u);
-}
-
-static void complete_move(Set **s, Set **other) {
- assert(s);
- assert(other);
-
- if (!*other)
- return;
-
- if (*s)
- set_move(*s, *other);
- else {
- *s = *other;
- *other = NULL;
- }
-}
-
-static void merge_names(Unit *u, Unit *other) {
- char *t;
- Iterator i;
-
- assert(u);
- assert(other);
-
- complete_move(&u->names, &other->names);
-
- set_free_free(other->names);
- other->names = NULL;
- other->id = NULL;
-
- SET_FOREACH(t, u->names, i)
- assert_se(hashmap_replace(u->manager->units, t, u) == 0);
-}
-
-static void merge_dependencies(Unit *u, Unit *other, UnitDependency d) {
- Iterator i;
- Unit *back;
- int r;
-
- assert(u);
- assert(other);
- assert(d < _UNIT_DEPENDENCY_MAX);
-
- /* Fix backwards pointers */
- SET_FOREACH(back, other->dependencies[d], i) {
- UnitDependency k;
-
- for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
- if ((r = set_remove_and_put(back->dependencies[k], other, u)) < 0) {
-
- if (r == -EEXIST)
- set_remove(back->dependencies[k], other);
- else
- assert(r == -ENOENT);
- }
- }
-
- complete_move(&u->dependencies[d], &other->dependencies[d]);
-
- set_free(other->dependencies[d]);
- other->dependencies[d] = NULL;
-}
-
-int unit_merge(Unit *u, Unit *other) {
- UnitDependency d;
-
- assert(u);
- assert(other);
- assert(u->manager == other->manager);
- assert(u->type != _UNIT_TYPE_INVALID);
-
- other = unit_follow_merge(other);
-
- if (other == u)
- return 0;
-
- if (u->type != other->type)
- return -EINVAL;
-
- if (!u->instance != !other->instance)
- return -EINVAL;
-
- if (other->load_state != UNIT_STUB &&
- other->load_state != UNIT_ERROR)
- return -EEXIST;
-
- if (other->job)
- return -EEXIST;
-
- if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
- return -EEXIST;
-
- /* Merge names */
- merge_names(u, other);
-
- /* Redirect all references */
- while (other->refs)
- unit_ref_set(other->refs, u);
-
- /* Merge dependencies */
- for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
- merge_dependencies(u, other, d);
-
- other->load_state = UNIT_MERGED;
- other->merged_into = u;
-
- /* If there is still some data attached to the other node, we
- * don't need it anymore, and can free it. */
- if (other->load_state != UNIT_STUB)
- if (UNIT_VTABLE(other)->done)
- UNIT_VTABLE(other)->done(other);
-
- unit_add_to_dbus_queue(u);
- unit_add_to_cleanup_queue(other);
-
- return 0;
-}
-
-int unit_merge_by_name(Unit *u, const char *name) {
- Unit *other;
- int r;
- char *s = NULL;
-
- assert(u);
- assert(name);
-
- if (unit_name_is_template(name)) {
- if (!u->instance)
- return -EINVAL;
-
- if (!(s = unit_name_replace_instance(name, u->instance)))
- return -ENOMEM;
-
- name = s;
- }
-
- if (!(other = manager_get_unit(u->manager, name)))
- r = unit_add_name(u, name);
- else
- r = unit_merge(u, other);
-
- free(s);
- return r;
-}
-
-Unit* unit_follow_merge(Unit *u) {
- assert(u);
-
- while (u->load_state == UNIT_MERGED)
- assert_se(u = u->merged_into);
-
- return u;
-}
-
-int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
- int r;
-
- assert(u);
- assert(c);
-
- if (c->std_output != EXEC_OUTPUT_KMSG &&
- c->std_output != EXEC_OUTPUT_SYSLOG &&
- c->std_output != EXEC_OUTPUT_JOURNAL &&
- c->std_output != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_SYSLOG_AND_CONSOLE &&
- c->std_output != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_KMSG &&
- c->std_error != EXEC_OUTPUT_SYSLOG &&
- c->std_error != EXEC_OUTPUT_JOURNAL &&
- c->std_error != EXEC_OUTPUT_KMSG_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_JOURNAL_AND_CONSOLE &&
- c->std_error != EXEC_OUTPUT_SYSLOG_AND_CONSOLE)
- return 0;
-
- /* If syslog or kernel logging is requested, make sure our own
- * logging daemon is run first. */
-
- if (u->manager->running_as == MANAGER_SYSTEM)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
- return r;
-
- return 0;
-}
-
-const char *unit_description(Unit *u) {
- assert(u);
-
- if (u->description)
- return u->description;
-
- return strna(u->id);
-}
-
-void unit_dump(Unit *u, FILE *f, const char *prefix) {
- char *t;
- UnitDependency d;
- Iterator i;
- char *p2;
- const char *prefix2;
- char
- timestamp1[FORMAT_TIMESTAMP_MAX],
- timestamp2[FORMAT_TIMESTAMP_MAX],
- timestamp3[FORMAT_TIMESTAMP_MAX],
- timestamp4[FORMAT_TIMESTAMP_MAX],
- timespan[FORMAT_TIMESPAN_MAX];
- Unit *following;
-
- assert(u);
- assert(u->type >= 0);
-
- if (!prefix)
- prefix = "";
- p2 = strappend(prefix, "\t");
- prefix2 = p2 ? p2 : prefix;
-
- fprintf(f,
- "%s-> Unit %s:\n"
- "%s\tDescription: %s\n"
- "%s\tInstance: %s\n"
- "%s\tUnit Load State: %s\n"
- "%s\tUnit Active State: %s\n"
- "%s\tInactive Exit Timestamp: %s\n"
- "%s\tActive Enter Timestamp: %s\n"
- "%s\tActive Exit Timestamp: %s\n"
- "%s\tInactive Enter Timestamp: %s\n"
- "%s\tGC Check Good: %s\n"
- "%s\tNeed Daemon Reload: %s\n",
- prefix, u->id,
- prefix, unit_description(u),
- prefix, strna(u->instance),
- prefix, unit_load_state_to_string(u->load_state),
- prefix, unit_active_state_to_string(unit_active_state(u)),
- prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)),
- prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
- prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
- prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
- prefix, yes_no(unit_check_gc(u)),
- prefix, yes_no(unit_need_daemon_reload(u)));
-
- SET_FOREACH(t, u->names, i)
- fprintf(f, "%s\tName: %s\n", prefix, t);
-
- if ((following = unit_following(u)))
- fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
-
- if (u->fragment_path)
- fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
-
- if (u->job_timeout > 0)
- fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
-
- condition_dump_list(u->conditions, f, prefix);
-
- if (dual_timestamp_is_set(&u->condition_timestamp))
- fprintf(f,
- "%s\tCondition Timestamp: %s\n"
- "%s\tCondition Result: %s\n",
- prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)),
- prefix, yes_no(u->condition_result));
-
- for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
- Unit *other;
-
- SET_FOREACH(other, u->dependencies[d], i)
- fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id);
- }
-
- if (u->load_state == UNIT_LOADED) {
- CGroupBonding *b;
- CGroupAttribute *a;
-
- fprintf(f,
- "%s\tStopWhenUnneeded: %s\n"
- "%s\tRefuseManualStart: %s\n"
- "%s\tRefuseManualStop: %s\n"
- "%s\tDefaultDependencies: %s\n"
- "%s\tOnFailureIsolate: %s\n"
- "%s\tIgnoreOnIsolate: %s\n"
- "%s\tIgnoreOnSnapshot: %s\n",
- prefix, yes_no(u->stop_when_unneeded),
- prefix, yes_no(u->refuse_manual_start),
- prefix, yes_no(u->refuse_manual_stop),
- prefix, yes_no(u->default_dependencies),
- prefix, yes_no(u->on_failure_isolate),
- prefix, yes_no(u->ignore_on_isolate),
- prefix, yes_no(u->ignore_on_snapshot));
-
- LIST_FOREACH(by_unit, b, u->cgroup_bondings)
- fprintf(f, "%s\tControlGroup: %s:%s\n",
- prefix, b->controller, b->path);
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
-
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
-
- fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
- prefix, a->controller, a->name, v ? v : a->value);
-
- free(v);
- }
-
- if (UNIT_VTABLE(u)->dump)
- UNIT_VTABLE(u)->dump(u, f, prefix2);
-
- } else if (u->load_state == UNIT_MERGED)
- fprintf(f,
- "%s\tMerged into: %s\n",
- prefix, u->merged_into->id);
- else if (u->load_state == UNIT_ERROR)
- fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
-
-
- if (u->job)
- job_dump(u->job, f, prefix2);
-
- free(p2);
-}
-
-/* Common implementation for multiple backends */
-int unit_load_fragment_and_dropin(Unit *u) {
- int r;
-
- assert(u);
-
- /* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_STUB)
- return -ENOENT;
-
- /* Load drop-in directory data */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
- return r;
-
- return 0;
-}
-
-/* Common implementation for multiple backends */
-int unit_load_fragment_and_dropin_optional(Unit *u) {
- int r;
-
- assert(u);
-
- /* Same as unit_load_fragment_and_dropin(), but whether
- * something can be loaded or not doesn't matter. */
-
- /* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
- return r;
-
- if (u->load_state == UNIT_STUB)
- u->load_state = UNIT_LOADED;
-
- /* Load drop-in directory data */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
- return r;
-
- return 0;
-}
-
-int unit_add_default_target_dependency(Unit *u, Unit *target) {
- assert(u);
- assert(target);
-
- if (target->type != UNIT_TARGET)
- return 0;
-
- /* Only add the dependency if both units are loaded, so that
- * that loop check below is reliable */
- if (u->load_state != UNIT_LOADED ||
- target->load_state != UNIT_LOADED)
- return 0;
-
- /* If either side wants no automatic dependencies, then let's
- * skip this */
- if (!u->default_dependencies ||
- !target->default_dependencies)
- return 0;
-
- /* Don't create loops */
- if (set_get(target->dependencies[UNIT_BEFORE], u))
- return 0;
-
- return unit_add_dependency(target, UNIT_AFTER, u, true);
-}
-
-static int unit_add_default_dependencies(Unit *u) {
- static const UnitDependency deps[] = {
- UNIT_REQUIRED_BY,
- UNIT_REQUIRED_BY_OVERRIDABLE,
- UNIT_WANTED_BY,
- UNIT_BOUND_BY
- };
-
- Unit *target;
- Iterator i;
- int r;
- unsigned k;
-
- assert(u);
-
- for (k = 0; k < ELEMENTSOF(deps); k++)
- SET_FOREACH(target, u->dependencies[deps[k]], i)
- if ((r = unit_add_default_target_dependency(u, target)) < 0)
- return r;
-
- return 0;
-}
-
-int unit_load(Unit *u) {
- int r;
-
- assert(u);
-
- if (u->in_load_queue) {
- LIST_REMOVE(Unit, load_queue, u->manager->load_queue, u);
- u->in_load_queue = false;
- }
-
- if (u->type == _UNIT_TYPE_INVALID)
- return -EINVAL;
-
- if (u->load_state != UNIT_STUB)
- return 0;
-
- if (UNIT_VTABLE(u)->load)
- if ((r = UNIT_VTABLE(u)->load(u)) < 0)
- goto fail;
-
- if (u->load_state == UNIT_STUB) {
- r = -ENOENT;
- goto fail;
- }
-
- if (u->load_state == UNIT_LOADED &&
- u->default_dependencies)
- if ((r = unit_add_default_dependencies(u)) < 0)
- goto fail;
-
- if (u->on_failure_isolate &&
- set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
-
- log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.",
- u->id);
-
- r = -EINVAL;
- goto fail;
- }
-
- assert((u->load_state != UNIT_MERGED) == !u->merged_into);
-
- unit_add_to_dbus_queue(unit_follow_merge(u));
- unit_add_to_gc_queue(u);
-
- return 0;
-
-fail:
- u->load_state = UNIT_ERROR;
- u->load_error = r;
- unit_add_to_dbus_queue(u);
- unit_add_to_gc_queue(u);
-
- log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r));
-
- return r;
-}
-
-bool unit_condition_test(Unit *u) {
- assert(u);
-
- dual_timestamp_get(&u->condition_timestamp);
- u->condition_result = condition_test_list(u->conditions);
-
- return u->condition_result;
-}
-
-/* Errors:
- * -EBADR: This unit type does not support starting.
- * -EALREADY: Unit is already started.
- * -EAGAIN: An operation is already in progress. Retry later.
- * -ECANCELED: Too many requests for now.
- */
-int unit_start(Unit *u) {
- UnitActiveState state;
- Unit *following;
-
- assert(u);
-
- if (u->load_state != UNIT_LOADED)
- return -EINVAL;
-
- /* If this is already started, then this will succeed. Note
- * that this will even succeed if this unit is not startable
- * by the user. This is relied on to detect when we need to
- * wait for units and when waiting is finished. */
- state = unit_active_state(u);
- if (UNIT_IS_ACTIVE_OR_RELOADING(state))
- return -EALREADY;
-
- /* If the conditions failed, don't do anything at all. If we
- * already are activating this call might still be useful to
- * speed up activation in case there is some hold-off time,
- * but we don't want to recheck the condition in that case. */
- if (state != UNIT_ACTIVATING &&
- !unit_condition_test(u)) {
- log_debug("Starting of %s requested but condition failed. Ignoring.", u->id);
- return -EALREADY;
- }
-
- /* Forward to the main object, if we aren't it. */
- if ((following = unit_following(u))) {
- log_debug("Redirecting start request from %s to %s.", u->id, following->id);
- return unit_start(following);
- }
-
- /* If it is stopped, but we cannot start it, then fail */
- if (!UNIT_VTABLE(u)->start)
- return -EBADR;
-
- /* We don't suppress calls to ->start() here when we are
- * already starting, to allow this request to be used as a
- * "hurry up" call, for example when the unit is in some "auto
- * restart" state where it waits for a holdoff timer to elapse
- * before it will start again. */
-
- unit_add_to_dbus_queue(u);
-
- unit_status_printf(u, NULL, "Starting %s...", unit_description(u));
- return UNIT_VTABLE(u)->start(u);
-}
-
-bool unit_can_start(Unit *u) {
- assert(u);
-
- return !!UNIT_VTABLE(u)->start;
-}
-
-bool unit_can_isolate(Unit *u) {
- assert(u);
-
- return unit_can_start(u) &&
- u->allow_isolate;
-}
-
-/* Errors:
- * -EBADR: This unit type does not support stopping.
- * -EALREADY: Unit is already stopped.
- * -EAGAIN: An operation is already in progress. Retry later.
- */
-int unit_stop(Unit *u) {
- UnitActiveState state;
- Unit *following;
-
- assert(u);
-
- state = unit_active_state(u);
- if (UNIT_IS_INACTIVE_OR_FAILED(state))
- return -EALREADY;
-
- if ((following = unit_following(u))) {
- log_debug("Redirecting stop request from %s to %s.", u->id, following->id);
- return unit_stop(following);
- }
-
- if (!UNIT_VTABLE(u)->stop)
- return -EBADR;
-
- unit_add_to_dbus_queue(u);
-
- unit_status_printf(u, NULL, "Stopping %s...", unit_description(u));
- return UNIT_VTABLE(u)->stop(u);
-}
-
-/* Errors:
- * -EBADR: This unit type does not support reloading.
- * -ENOEXEC: Unit is not started.
- * -EAGAIN: An operation is already in progress. Retry later.
- */
-int unit_reload(Unit *u) {
- UnitActiveState state;
- Unit *following;
-
- assert(u);
-
- if (u->load_state != UNIT_LOADED)
- return -EINVAL;
-
- if (!unit_can_reload(u))
- return -EBADR;
-
- state = unit_active_state(u);
- if (state == UNIT_RELOADING)
- return -EALREADY;
-
- if (state != UNIT_ACTIVE)
- return -ENOEXEC;
-
- if ((following = unit_following(u))) {
- log_debug("Redirecting reload request from %s to %s.", u->id, following->id);
- return unit_reload(following);
- }
-
- unit_add_to_dbus_queue(u);
- return UNIT_VTABLE(u)->reload(u);
-}
-
-bool unit_can_reload(Unit *u) {
- assert(u);
-
- if (!UNIT_VTABLE(u)->reload)
- return false;
-
- if (!UNIT_VTABLE(u)->can_reload)
- return true;
-
- return UNIT_VTABLE(u)->can_reload(u);
-}
-
-static void unit_check_unneeded(Unit *u) {
- Iterator i;
- Unit *other;
-
- assert(u);
-
- /* If this service shall be shut down when unneeded then do
- * so. */
-
- if (!u->stop_when_unneeded)
- return;
-
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (unit_pending_active(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (unit_pending_active(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
- if (unit_pending_active(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (unit_pending_active(other))
- return;
-
- log_info("Service %s is not needed anymore. Stopping.", u->id);
-
- /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
-}
-
-static void retroactively_start_dependencies(Unit *u) {
- Iterator i;
- Unit *other;
-
- assert(u);
- assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
-}
-
-static void retroactively_stop_dependencies(Unit *u) {
- Iterator i;
- Unit *other;
-
- assert(u);
- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
-
- /* Pull down units which are bound to us recursively if enabled */
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
-}
-
-static void check_unneeded_dependencies(Unit *u) {
- Iterator i;
- Unit *other;
-
- assert(u);
- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
-
- /* Garbage collect services that might not be needed anymore, if enabled */
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_BIND_TO], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
-}
-
-void unit_trigger_on_failure(Unit *u) {
- Unit *other;
- Iterator i;
-
- assert(u);
-
- if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
- return;
-
- log_info("Triggering OnFailure= dependencies of %s.", u->id);
-
- SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
- int r;
-
- if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0)
- log_error("Failed to enqueue OnFailure= job: %s", strerror(-r));
- }
-}
-
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
- bool unexpected;
-
- assert(u);
- assert(os < _UNIT_ACTIVE_STATE_MAX);
- assert(ns < _UNIT_ACTIVE_STATE_MAX);
-
- /* Note that this is called for all low-level state changes,
- * even if they might map to the same high-level
- * UnitActiveState! That means that ns == os is OK an expected
- * behaviour here. For example: if a mount point is remounted
- * this function will be called too! */
-
- if (u->manager->n_reloading <= 0) {
- dual_timestamp ts;
-
- dual_timestamp_get(&ts);
-
- if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_exit_timestamp = ts;
- else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_enter_timestamp = ts;
-
- if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_enter_timestamp = ts;
- else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_exit_timestamp = ts;
-
- timer_unit_notify(u, ns);
- path_unit_notify(u, ns);
- }
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- cgroup_bonding_trim_list(u->cgroup_bondings, true);
-
- if (u->job) {
- unexpected = false;
-
- if (u->job->state == JOB_WAITING)
-
- /* So we reached a different state for this
- * job. Let's see if we can run it now if it
- * failed previously due to EAGAIN. */
- job_add_to_run_queue(u->job);
-
- /* Let's check whether this state change constitutes a
- * finished job, or maybe contradicts a running job and
- * hence needs to invalidate jobs. */
-
- switch (u->job->type) {
-
- case JOB_START:
- case JOB_VERIFY_ACTIVE:
-
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
- job_finish_and_invalidate(u->job, JOB_DONE);
- else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
- unexpected = true;
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
- }
-
- break;
-
- case JOB_RELOAD:
- case JOB_RELOAD_OR_START:
-
- if (u->job->state == JOB_RUNNING) {
- if (ns == UNIT_ACTIVE)
- job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED);
- else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) {
- unexpected = true;
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE);
- }
- }
-
- break;
-
- case JOB_STOP:
- case JOB_RESTART:
- case JOB_TRY_RESTART:
-
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, JOB_DONE);
- else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
- unexpected = true;
- job_finish_and_invalidate(u->job, JOB_FAILED);
- }
-
- break;
-
- default:
- assert_not_reached("Job type unknown");
- }
-
- } else
- unexpected = true;
-
- if (u->manager->n_reloading <= 0) {
-
- /* If this state change happened without being
- * requested by a job, then let's retroactively start
- * or stop dependencies. We skip that step when
- * deserializing, since we don't want to create any
- * additional jobs just because something is already
- * activated. */
-
- if (unexpected) {
- if (UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_ACTIVE_OR_ACTIVATING(ns))
- retroactively_start_dependencies(u);
- else if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
- retroactively_stop_dependencies(u);
- }
-
- /* stop unneeded units regardless if going down was expected or not */
- if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns))
- check_unneeded_dependencies(u);
-
- if (ns != os && ns == UNIT_FAILED) {
- log_notice("Unit %s entered failed state.", u->id);
- unit_trigger_on_failure(u);
- }
- }
-
- /* Some names are special */
- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) {
-
- if (unit_has_name(u, SPECIAL_DBUS_SERVICE))
- /* The bus just might have become available,
- * hence try to connect to it, if we aren't
- * yet connected. */
- bus_init(u->manager, true);
-
- if (u->type == UNIT_SERVICE &&
- !UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- u->manager->n_reloading <= 0) {
- /* Write audit record if we have just finished starting up */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
- u->in_audit = true;
- }
-
- if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
- manager_send_unit_plymouth(u->manager, u);
-
- } else {
-
- /* We don't care about D-Bus here, since we'll get an
- * asynchronous notification for it anyway. */
-
- if (u->type == UNIT_SERVICE &&
- UNIT_IS_INACTIVE_OR_FAILED(ns) &&
- !UNIT_IS_INACTIVE_OR_FAILED(os) &&
- u->manager->n_reloading <= 0) {
-
- /* Hmm, if there was no start record written
- * write it now, so that we always have a nice
- * pair */
- if (!u->in_audit) {
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
-
- if (ns == UNIT_INACTIVE)
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
- } else
- /* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
-
- u->in_audit = false;
- }
- }
-
- manager_recheck_journal(u->manager);
-
- /* Maybe we finished startup and are now ready for being
- * stopped because unneeded? */
- unit_check_unneeded(u);
-
- unit_add_to_dbus_queue(u);
- unit_add_to_gc_queue(u);
-}
-
-int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
- struct epoll_event ev;
-
- assert(u);
- assert(fd >= 0);
- assert(w);
- assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u));
-
- zero(ev);
- ev.data.ptr = w;
- ev.events = events;
-
- if (epoll_ctl(u->manager->epoll_fd,
- w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
- fd,
- &ev) < 0)
- return -errno;
-
- w->fd = fd;
- w->type = WATCH_FD;
- w->data.unit = u;
-
- return 0;
-}
-
-void unit_unwatch_fd(Unit *u, Watch *w) {
- assert(u);
- assert(w);
-
- if (w->type == WATCH_INVALID)
- return;
-
- assert(w->type == WATCH_FD);
- assert(w->data.unit == u);
- assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
-
- w->fd = -1;
- w->type = WATCH_INVALID;
- w->data.unit = NULL;
-}
-
-int unit_watch_pid(Unit *u, pid_t pid) {
- assert(u);
- assert(pid >= 1);
-
- /* Watch a specific PID. We only support one unit watching
- * each PID for now. */
-
- return hashmap_put(u->manager->watch_pids, LONG_TO_PTR(pid), u);
-}
-
-void unit_unwatch_pid(Unit *u, pid_t pid) {
- assert(u);
- assert(pid >= 1);
-
- hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u);
-}
-
-int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
- struct itimerspec its;
- int flags, fd;
- bool ours;
-
- assert(u);
- assert(w);
- assert(w->type == WATCH_INVALID || (w->type == WATCH_UNIT_TIMER && w->data.unit == u));
-
- /* This will try to reuse the old timer if there is one */
-
- if (w->type == WATCH_UNIT_TIMER) {
- assert(w->data.unit == u);
- assert(w->fd >= 0);
-
- ours = false;
- fd = w->fd;
- } else if (w->type == WATCH_INVALID) {
-
- ours = true;
- if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
- return -errno;
- } else
- assert_not_reached("Invalid watch type");
-
- zero(its);
-
- if (delay <= 0) {
- /* Set absolute time in the past, but not 0, since we
- * don't want to disarm the timer */
- its.it_value.tv_sec = 0;
- its.it_value.tv_nsec = 1;
-
- flags = TFD_TIMER_ABSTIME;
- } else {
- timespec_store(&its.it_value, delay);
- flags = 0;
- }
-
- /* This will also flush the elapse counter */
- if (timerfd_settime(fd, flags, &its, NULL) < 0)
- goto fail;
-
- if (w->type == WATCH_INVALID) {
- struct epoll_event ev;
-
- zero(ev);
- ev.data.ptr = w;
- ev.events = EPOLLIN;
-
- if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
- goto fail;
- }
-
- w->type = WATCH_UNIT_TIMER;
- w->fd = fd;
- w->data.unit = u;
-
- return 0;
-
-fail:
- if (ours)
- close_nointr_nofail(fd);
-
- return -errno;
-}
-
-void unit_unwatch_timer(Unit *u, Watch *w) {
- assert(u);
- assert(w);
-
- if (w->type == WATCH_INVALID)
- return;
-
- assert(w->type == WATCH_UNIT_TIMER);
- assert(w->data.unit == u);
- assert(w->fd >= 0);
-
- assert_se(epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
- close_nointr_nofail(w->fd);
-
- w->fd = -1;
- w->type = WATCH_INVALID;
- w->data.unit = NULL;
-}
-
-bool unit_job_is_applicable(Unit *u, JobType j) {
- assert(u);
- assert(j >= 0 && j < _JOB_TYPE_MAX);
-
- switch (j) {
-
- case JOB_VERIFY_ACTIVE:
- case JOB_START:
- case JOB_STOP:
- return true;
-
- case JOB_RESTART:
- case JOB_TRY_RESTART:
- return unit_can_start(u);
-
- case JOB_RELOAD:
- return unit_can_reload(u);
-
- case JOB_RELOAD_OR_START:
- return unit_can_reload(u) && unit_can_start(u);
-
- default:
- assert_not_reached("Invalid job type");
- }
-}
-
-int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
-
- static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
- [UNIT_REQUIRES] = UNIT_REQUIRED_BY,
- [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
- [UNIT_WANTS] = UNIT_WANTED_BY,
- [UNIT_REQUISITE] = UNIT_REQUIRED_BY,
- [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
- [UNIT_BIND_TO] = UNIT_BOUND_BY,
- [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_BOUND_BY] = UNIT_BIND_TO,
- [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
- [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
- [UNIT_BEFORE] = UNIT_AFTER,
- [UNIT_AFTER] = UNIT_BEFORE,
- [UNIT_ON_FAILURE] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_REFERENCES] = UNIT_REFERENCED_BY,
- [UNIT_REFERENCED_BY] = UNIT_REFERENCES,
- [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY,
- [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS,
- [UNIT_PROPAGATE_RELOAD_TO] = UNIT_PROPAGATE_RELOAD_FROM,
- [UNIT_PROPAGATE_RELOAD_FROM] = UNIT_PROPAGATE_RELOAD_TO
- };
- int r, q = 0, v = 0, w = 0;
-
- assert(u);
- assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
- assert(other);
-
- u = unit_follow_merge(u);
- other = unit_follow_merge(other);
-
- /* We won't allow dependencies on ourselves. We will not
- * consider them an error however. */
- if (u == other)
- return 0;
-
- if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0)
- return r;
-
- if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
- if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
- return r;
-
- if (add_reference)
- if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 ||
- (r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0)
- return r;
-
- if ((q = set_put(u->dependencies[d], other)) < 0)
- return q;
-
- if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
- if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) {
- r = v;
- goto fail;
- }
-
- if (add_reference) {
- if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) {
- r = w;
- goto fail;
- }
-
- if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0)
- goto fail;
- }
-
- unit_add_to_dbus_queue(u);
- return 0;
-
-fail:
- if (q > 0)
- set_remove(u->dependencies[d], other);
-
- if (v > 0)
- set_remove(other->dependencies[inverse_table[d]], u);
-
- if (w > 0)
- set_remove(u->dependencies[UNIT_REFERENCES], other);
-
- return r;
-}
-
-int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) {
- int r;
-
- assert(u);
-
- if ((r = unit_add_dependency(u, d, other, add_reference)) < 0)
- return r;
-
- if ((r = unit_add_dependency(u, e, other, add_reference)) < 0)
- return r;
-
- return 0;
-}
-
-static const char *resolve_template(Unit *u, const char *name, const char*path, char **p) {
- char *s;
-
- assert(u);
- assert(name || path);
-
- if (!name)
- name = file_name_from_path(path);
-
- if (!unit_name_is_template(name)) {
- *p = NULL;
- return name;
- }
-
- if (u->instance)
- s = unit_name_replace_instance(name, u->instance);
- else {
- char *i;
-
- if (!(i = unit_name_to_prefix(u->id)))
- return NULL;
-
- s = unit_name_replace_instance(name, i);
- free(i);
- }
-
- if (!s)
- return NULL;
-
- *p = s;
- return s;
-}
-
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
- Unit *other;
- int r;
- char *s;
-
- assert(u);
- assert(name || path);
-
- if (!(name = resolve_template(u, name, path, &s)))
- return -ENOMEM;
-
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- r = unit_add_dependency(u, d, other, add_reference);
-
-finish:
- free(s);
- return r;
-}
-
-int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
- Unit *other;
- int r;
- char *s;
-
- assert(u);
- assert(name || path);
-
- if (!(name = resolve_template(u, name, path, &s)))
- return -ENOMEM;
-
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- r = unit_add_two_dependencies(u, d, e, other, add_reference);
-
-finish:
- free(s);
- return r;
-}
-
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
- Unit *other;
- int r;
- char *s;
-
- assert(u);
- assert(name || path);
-
- if (!(name = resolve_template(u, name, path, &s)))
- return -ENOMEM;
-
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- r = unit_add_dependency(other, d, u, add_reference);
-
-finish:
- free(s);
- return r;
-}
-
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
- Unit *other;
- int r;
- char *s;
-
- assert(u);
- assert(name || path);
-
- if (!(name = resolve_template(u, name, path, &s)))
- return -ENOMEM;
-
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0)
- goto finish;
-
-finish:
- free(s);
- return r;
-}
-
-int set_unit_path(const char *p) {
- char *cwd, *c;
- int r;
-
- /* This is mostly for debug purposes */
-
- if (path_is_absolute(p)) {
- if (!(c = strdup(p)))
- return -ENOMEM;
- } else {
- if (!(cwd = get_current_dir_name()))
- return -errno;
-
- r = asprintf(&c, "%s/%s", cwd, p);
- free(cwd);
-
- if (r < 0)
- return -ENOMEM;
- }
-
- if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
- r = -errno;
- free(c);
- return r;
- }
-
- return 0;
-}
-
-char *unit_dbus_path(Unit *u) {
- char *p, *e;
-
- assert(u);
-
- if (!u->id)
- return NULL;
-
- if (!(e = bus_path_escape(u->id)))
- return NULL;
-
- p = strappend("/org/freedesktop/systemd1/unit/", e);
- free(e);
-
- return p;
-}
-
-int unit_add_cgroup(Unit *u, CGroupBonding *b) {
- int r;
-
- assert(u);
- assert(b);
-
- assert(b->path);
-
- if (!b->controller) {
- if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER)))
- return -ENOMEM;
-
- b->ours = true;
- }
-
- /* Ensure this hasn't been added yet */
- assert(!b->unit);
-
- if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
- CGroupBonding *l;
-
- l = hashmap_get(u->manager->cgroup_bondings, b->path);
- LIST_PREPEND(CGroupBonding, by_path, l, b);
-
- if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) {
- LIST_REMOVE(CGroupBonding, by_path, l, b);
- return r;
- }
- }
-
- LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b);
- b->unit = u;
-
- return 0;
-}
-
-static char *default_cgroup_path(Unit *u) {
- char *p;
-
- assert(u);
-
- if (u->instance) {
- char *t;
-
- t = unit_name_template(u->id);
- if (!t)
- return NULL;
-
- p = join(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
- free(t);
- } else
- p = join(u->manager->cgroup_hierarchy, "/", u->id, NULL);
-
- return p;
-}
-
-int unit_add_cgroup_from_text(Unit *u, const char *name) {
- char *controller = NULL, *path = NULL;
- CGroupBonding *b = NULL;
- bool ours = false;
- int r;
-
- assert(u);
- assert(name);
-
- if ((r = cg_split_spec(name, &controller, &path)) < 0)
- return r;
-
- if (!path) {
- path = default_cgroup_path(u);
- ours = true;
- }
-
- if (!controller) {
- controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
- ours = true;
- }
-
- if (!path || !controller) {
- free(path);
- free(controller);
-
- return -ENOMEM;
- }
-
- if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) {
- r = -EEXIST;
- goto fail;
- }
-
- if (!(b = new0(CGroupBonding, 1))) {
- r = -ENOMEM;
- goto fail;
- }
-
- b->controller = controller;
- b->path = path;
- b->ours = ours;
- b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
-
- if ((r = unit_add_cgroup(u, b)) < 0)
- goto fail;
-
- return 0;
-
-fail:
- free(path);
- free(controller);
- free(b);
-
- return r;
-}
-
-static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
- CGroupBonding *b = NULL;
- int r = -ENOMEM;
-
- assert(u);
-
- if (!controller)
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
- if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
- return 0;
-
- if (!(b = new0(CGroupBonding, 1)))
- return -ENOMEM;
-
- if (!(b->controller = strdup(controller)))
- goto fail;
-
- if (!(b->path = default_cgroup_path(u)))
- goto fail;
-
- b->ours = true;
- b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
-
- if ((r = unit_add_cgroup(u, b)) < 0)
- goto fail;
-
- return 0;
-
-fail:
- free(b->path);
- free(b->controller);
- free(b);
-
- return r;
-}
-
-int unit_add_default_cgroups(Unit *u) {
- CGroupAttribute *a;
- char **c;
- int r;
-
- assert(u);
-
- /* Adds in the default cgroups, if they weren't specified
- * otherwise. */
-
- if (!u->manager->cgroup_hierarchy)
- return 0;
-
- if ((r = unit_add_one_default_cgroup(u, NULL)) < 0)
- return r;
-
- STRV_FOREACH(c, u->manager->default_controllers)
- unit_add_one_default_cgroup(u, *c);
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes)
- unit_add_one_default_cgroup(u, a->controller);
-
- return 0;
-}
-
-CGroupBonding* unit_get_default_cgroup(Unit *u) {
- assert(u);
-
- return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
-}
-
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
- int r;
- char *c = NULL;
- CGroupAttribute *a;
-
- assert(u);
- assert(name);
- assert(value);
-
- if (!controller) {
- const char *dot;
-
- dot = strchr(name, '.');
- if (!dot)
- return -EINVAL;
-
- c = strndup(name, dot - name);
- if (!c)
- return -ENOMEM;
-
- controller = c;
- }
-
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = -EINVAL;
- goto finish;
- }
-
- a = new0(CGroupAttribute, 1);
- if (!a) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (c) {
- a->controller = c;
- c = NULL;
- } else
- a->controller = strdup(controller);
-
- a->name = strdup(name);
- a->value = strdup(value);
-
- if (!a->controller || !a->name || !a->value) {
- free(a->controller);
- free(a->name);
- free(a->value);
- free(a);
-
- return -ENOMEM;
- }
-
- a->map_callback = map_callback;
-
- LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
-
- r = 0;
-
-finish:
- free(c);
- return r;
-}
-
-int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
- char *t;
- int r;
-
- assert(u);
- assert(type);
- assert(_found);
-
- if (!(t = unit_name_change_suffix(u->id, type)))
- return -ENOMEM;
-
- assert(!unit_has_name(u, t));
-
- r = manager_load_unit(u->manager, t, NULL, NULL, _found);
- free(t);
-
- assert(r < 0 || *_found != u);
-
- return r;
-}
-
-int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
- Unit *found;
- char *t;
-
- assert(u);
- assert(type);
- assert(_found);
-
- if (!(t = unit_name_change_suffix(u->id, type)))
- return -ENOMEM;
-
- assert(!unit_has_name(u, t));
-
- found = manager_get_unit(u->manager, t);
- free(t);
-
- if (!found)
- return -ENOENT;
-
- *_found = found;
- return 0;
-}
-
-static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return unit_name_to_prefix_and_instance(u->id);
-}
-
-static char *specifier_prefix(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return unit_name_to_prefix(u->id);
-}
-
-static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- char *p, *r;
-
- assert(u);
-
- if (!(p = unit_name_to_prefix(u->id)))
- return NULL;
-
- r = unit_name_unescape(p);
- free(p);
-
- return r;
-}
-
-static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->instance)
- return unit_name_unescape(u->instance);
-
- return strdup("");
-}
-
-static char *specifier_filename(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->instance)
- return unit_name_path_unescape(u->instance);
-
- return unit_name_to_path(u->instance);
-}
-
-static char *specifier_cgroup(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return default_cgroup_path(u);
-}
-
-static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- char *p;
- assert(u);
-
- if (specifier == 'r')
- return strdup(u->manager->cgroup_hierarchy);
-
- if (parent_of_path(u->manager->cgroup_hierarchy, &p) < 0)
- return strdup("");
-
- if (streq(p, "/")) {
- free(p);
- return strdup("");
- }
-
- return p;
-}
-
-static char *specifier_runtime(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->manager->running_as == MANAGER_USER) {
- const char *e;
-
- e = getenv("XDG_RUNTIME_DIR");
- if (e)
- return strdup(e);
- }
-
- return strdup("/run");
-}
-
-char *unit_name_printf(Unit *u, const char* format) {
-
- /*
- * This will use the passed string as format string and
- * replace the following specifiers:
- *
- * %n: the full id of the unit (foo at bar.waldo)
- * %N: the id of the unit without the suffix (foo at bar)
- * %p: the prefix (foo)
- * %i: the instance (bar)
- */
-
- const Specifier table[] = {
- { 'n', specifier_string, u->id },
- { 'N', specifier_prefix_and_instance, NULL },
- { 'p', specifier_prefix, NULL },
- { 'i', specifier_string, u->instance },
- { 0, NULL, NULL }
- };
-
- assert(u);
- assert(format);
-
- return specifier_printf(format, table, u);
-}
-
-char *unit_full_printf(Unit *u, const char *format) {
-
- /* This is similar to unit_name_printf() but also supports
- * unescaping. Also, adds a couple of additional codes:
- *
- * %c cgroup path of unit
- * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
- * %R parent of root cgroup path (e.g. "/usr/lennart/shared")
- * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
- */
-
- const Specifier table[] = {
- { 'n', specifier_string, u->id },
- { 'N', specifier_prefix_and_instance, NULL },
- { 'p', specifier_prefix, NULL },
- { 'P', specifier_prefix_unescaped, NULL },
- { 'i', specifier_string, u->instance },
- { 'I', specifier_instance_unescaped, NULL },
- { 'f', specifier_filename, NULL },
- { 'c', specifier_cgroup, NULL },
- { 'r', specifier_cgroup_root, NULL },
- { 'R', specifier_cgroup_root, NULL },
- { 't', specifier_runtime, NULL },
- { 0, NULL, NULL }
- };
-
- assert(u);
- assert(format);
-
- return specifier_printf(format, table, u);
-}
-
-char **unit_full_printf_strv(Unit *u, char **l) {
- size_t n;
- char **r, **i, **j;
-
- /* Applies unit_full_printf to every entry in l */
-
- assert(u);
-
- n = strv_length(l);
- if (!(r = new(char*, n+1)))
- return NULL;
-
- for (i = l, j = r; *i; i++, j++)
- if (!(*j = unit_full_printf(u, *i)))
- goto fail;
-
- *j = NULL;
- return r;
-
-fail:
- for (j--; j >= r; j--)
- free(*j);
-
- free(r);
-
- return NULL;
-}
-
-int unit_watch_bus_name(Unit *u, const char *name) {
- assert(u);
- assert(name);
-
- /* Watch a specific name on the bus. We only support one unit
- * watching each name for now. */
-
- return hashmap_put(u->manager->watch_bus, name, u);
-}
-
-void unit_unwatch_bus_name(Unit *u, const char *name) {
- assert(u);
- assert(name);
-
- hashmap_remove_value(u->manager->watch_bus, name, u);
-}
-
-bool unit_can_serialize(Unit *u) {
- assert(u);
-
- return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
-}
-
-int unit_serialize(Unit *u, FILE *f, FDSet *fds) {
- int r;
-
- assert(u);
- assert(f);
- assert(fds);
-
- if (!unit_can_serialize(u))
- return 0;
-
- if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
- return r;
-
- if (u->job)
- unit_serialize_item(u, f, "job", job_type_to_string(u->job->type));
-
- dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
- dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
- dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
- dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
- dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
-
- if (dual_timestamp_is_set(&u->condition_timestamp))
- unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
-
- /* End marker */
- fputc('\n', f);
- return 0;
-}
-
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(f);
- assert(key);
- assert(format);
-
- fputs(key, f);
- fputc('=', f);
-
- va_start(ap, format);
- vfprintf(f, format, ap);
- va_end(ap);
-
- fputc('\n', f);
-}
-
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
- assert(u);
- assert(f);
- assert(key);
- assert(value);
-
- fprintf(f, "%s=%s\n", key, value);
-}
-
-int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
- int r;
-
- assert(u);
- assert(f);
- assert(fds);
-
- if (!unit_can_serialize(u))
- return 0;
-
- for (;;) {
- char line[LINE_MAX], *l, *v;
- size_t k;
-
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- return 0;
- return -errno;
- }
-
- char_array_0(line);
- l = strstrip(line);
-
- /* End marker */
- if (l[0] == 0)
- return 0;
-
- k = strcspn(l, "=");
-
- if (l[k] == '=') {
- l[k] = 0;
- v = l+k+1;
- } else
- v = l+k;
-
- if (streq(l, "job")) {
- JobType type;
-
- if ((type = job_type_from_string(v)) < 0)
- log_debug("Failed to parse job type value %s", v);
- else
- u->deserialized_job = type;
-
- continue;
- } else if (streq(l, "inactive-exit-timestamp")) {
- dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
- continue;
- } else if (streq(l, "active-enter-timestamp")) {
- dual_timestamp_deserialize(v, &u->active_enter_timestamp);
- continue;
- } else if (streq(l, "active-exit-timestamp")) {
- dual_timestamp_deserialize(v, &u->active_exit_timestamp);
- continue;
- } else if (streq(l, "inactive-enter-timestamp")) {
- dual_timestamp_deserialize(v, &u->inactive_enter_timestamp);
- continue;
- } else if (streq(l, "condition-timestamp")) {
- dual_timestamp_deserialize(v, &u->condition_timestamp);
- continue;
- } else if (streq(l, "condition-result")) {
- int b;
-
- if ((b = parse_boolean(v)) < 0)
- log_debug("Failed to parse condition result value %s", v);
- else
- u->condition_result = b;
-
- continue;
- }
-
- if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
- return r;
- }
-}
-
-int unit_add_node_link(Unit *u, const char *what, bool wants) {
- Unit *device;
- char *e;
- int r;
-
- assert(u);
-
- if (!what)
- return 0;
-
- /* Adds in links to the device node that this unit is based on */
-
- if (!is_device_path(what))
- return 0;
-
- if (!(e = unit_name_build_escape(what+1, NULL, ".device")))
- return -ENOMEM;
-
- r = manager_load_unit(u->manager, e, NULL, NULL, &device);
- free(e);
-
- if (r < 0)
- return r;
-
- if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BIND_TO, device, true)) < 0)
- return r;
-
- if (wants)
- if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0)
- return r;
-
- return 0;
-}
-
-int unit_coldplug(Unit *u) {
- int r;
-
- assert(u);
-
- if (UNIT_VTABLE(u)->coldplug)
- if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
- return r;
-
- if (u->deserialized_job >= 0) {
- if ((r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL)) < 0)
- return r;
-
- u->deserialized_job = _JOB_TYPE_INVALID;
- }
-
- return 0;
-}
-
-void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(format);
-
- if (!UNIT_VTABLE(u)->show_status)
- return;
-
- if (!manager_get_show_status(u->manager))
- return;
-
- if (!manager_is_booting_or_shutting_down(u->manager))
- return;
-
- va_start(ap, format);
- status_vprintf(status, true, format, ap);
- va_end(ap);
-}
-
-bool unit_need_daemon_reload(Unit *u) {
- assert(u);
-
- if (u->fragment_path) {
- struct stat st;
-
- zero(st);
- if (stat(u->fragment_path, &st) < 0)
- /* What, cannot access this anymore? */
- return true;
-
- if (u->fragment_mtime > 0 &&
- timespec_load(&st.st_mtim) != u->fragment_mtime)
- return true;
- }
-
- if (UNIT_VTABLE(u)->need_daemon_reload)
- return UNIT_VTABLE(u)->need_daemon_reload(u);
-
- return false;
-}
-
-void unit_reset_failed(Unit *u) {
- assert(u);
-
- if (UNIT_VTABLE(u)->reset_failed)
- UNIT_VTABLE(u)->reset_failed(u);
-}
-
-Unit *unit_following(Unit *u) {
- assert(u);
-
- if (UNIT_VTABLE(u)->following)
- return UNIT_VTABLE(u)->following(u);
-
- return NULL;
-}
-
-bool unit_pending_inactive(Unit *u) {
- assert(u);
-
- /* Returns true if the unit is inactive or going down */
-
- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)))
- return true;
-
- if (u->job && u->job->type == JOB_STOP)
- return true;
-
- return false;
-}
-
-bool unit_pending_active(Unit *u) {
- assert(u);
-
- /* Returns true if the unit is active or going up */
-
- if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
- return true;
-
- if (u->job &&
- (u->job->type == JOB_START ||
- u->job->type == JOB_RELOAD_OR_START ||
- u->job->type == JOB_RESTART))
- return true;
-
- return false;
-}
-
-UnitType unit_name_to_type(const char *n) {
- UnitType t;
-
- assert(n);
-
- for (t = 0; t < _UNIT_TYPE_MAX; t++)
- if (endswith(n, unit_vtable[t]->suffix))
- return t;
-
- return _UNIT_TYPE_INVALID;
-}
-
-bool unit_name_is_valid(const char *n, bool template_ok) {
- UnitType t;
-
- t = unit_name_to_type(n);
- if (t < 0 || t >= _UNIT_TYPE_MAX)
- return false;
-
- return unit_name_is_valid_no_type(n, template_ok);
-}
-
-int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) {
- assert(u);
- assert(w >= 0 && w < _KILL_WHO_MAX);
- assert(m >= 0 && m < _KILL_MODE_MAX);
- assert(signo > 0);
- assert(signo < _NSIG);
-
- if (m == KILL_NONE)
- return 0;
-
- if (!UNIT_VTABLE(u)->kill)
- return -ENOTSUP;
-
- return UNIT_VTABLE(u)->kill(u, w, m, signo, error);
-}
-
-int unit_following_set(Unit *u, Set **s) {
- assert(u);
- assert(s);
-
- if (UNIT_VTABLE(u)->following_set)
- return UNIT_VTABLE(u)->following_set(u, s);
-
- *s = NULL;
- return 0;
-}
-
-UnitFileState unit_get_unit_file_state(Unit *u) {
- assert(u);
-
- if (u->unit_file_state < 0 && u->fragment_path)
- u->unit_file_state = unit_file_get_state(
- u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
- NULL, file_name_from_path(u->fragment_path));
-
- return u->unit_file_state;
-}
-
-Unit* unit_ref_set(UnitRef *ref, Unit *u) {
- assert(ref);
- assert(u);
-
- if (ref->unit)
- unit_ref_unset(ref);
-
- ref->unit = u;
- LIST_PREPEND(UnitRef, refs, u->refs, ref);
- return u;
-}
-
-void unit_ref_unset(UnitRef *ref) {
- assert(ref);
-
- if (!ref->unit)
- return;
-
- LIST_REMOVE(UnitRef, refs, ref->unit->refs, ref);
- ref->unit = NULL;
-}
-
-static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
- [UNIT_STUB] = "stub",
- [UNIT_LOADED] = "loaded",
- [UNIT_ERROR] = "error",
- [UNIT_MERGED] = "merged",
- [UNIT_MASKED] = "masked"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
-
-static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
- [UNIT_ACTIVE] = "active",
- [UNIT_RELOADING] = "reloading",
- [UNIT_INACTIVE] = "inactive",
- [UNIT_FAILED] = "failed",
- [UNIT_ACTIVATING] = "activating",
- [UNIT_DEACTIVATING] = "deactivating"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
-
-static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
- [UNIT_REQUIRES] = "Requires",
- [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
- [UNIT_WANTS] = "Wants",
- [UNIT_REQUISITE] = "Requisite",
- [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
- [UNIT_REQUIRED_BY] = "RequiredBy",
- [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
- [UNIT_BIND_TO] = "BindTo",
- [UNIT_WANTED_BY] = "WantedBy",
- [UNIT_CONFLICTS] = "Conflicts",
- [UNIT_CONFLICTED_BY] = "ConflictedBy",
- [UNIT_BOUND_BY] = "BoundBy",
- [UNIT_BEFORE] = "Before",
- [UNIT_AFTER] = "After",
- [UNIT_REFERENCES] = "References",
- [UNIT_REFERENCED_BY] = "ReferencedBy",
- [UNIT_ON_FAILURE] = "OnFailure",
- [UNIT_TRIGGERS] = "Triggers",
- [UNIT_TRIGGERED_BY] = "TriggeredBy",
- [UNIT_PROPAGATE_RELOAD_TO] = "PropagateReloadTo",
- [UNIT_PROPAGATE_RELOAD_FROM] = "PropagateReloadFrom"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
diff --git a/src/unit.h b/src/unit.h
deleted file mode 100644
index 756f465..0000000
--- a/src/unit.h
+++ /dev/null
@@ -1,562 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foounithfoo
-#define foounithfoo
-
-/***
- 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 General Public License as published by
- the Free Software Foundation; either version 2 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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <stdlib.h>
-
-typedef struct Unit Unit;
-typedef struct UnitVTable UnitVTable;
-typedef enum UnitType UnitType;
-typedef enum UnitLoadState UnitLoadState;
-typedef enum UnitActiveState UnitActiveState;
-typedef enum UnitDependency UnitDependency;
-typedef struct UnitRef UnitRef;
-
-#include "set.h"
-#include "util.h"
-#include "list.h"
-#include "socket-util.h"
-#include "execute.h"
-#include "condition.h"
-#include "install.h"
-
-enum UnitType {
- UNIT_SERVICE = 0,
- UNIT_SOCKET,
- UNIT_TARGET,
- UNIT_DEVICE,
- UNIT_MOUNT,
- UNIT_AUTOMOUNT,
- UNIT_SNAPSHOT,
- UNIT_TIMER,
- UNIT_SWAP,
- UNIT_PATH,
- _UNIT_TYPE_MAX,
- _UNIT_TYPE_INVALID = -1
-};
-
-enum UnitLoadState {
- UNIT_STUB,
- UNIT_LOADED,
- UNIT_ERROR,
- UNIT_MERGED,
- UNIT_MASKED,
- _UNIT_LOAD_STATE_MAX,
- _UNIT_LOAD_STATE_INVALID = -1
-};
-
-enum UnitActiveState {
- UNIT_ACTIVE,
- UNIT_RELOADING,
- UNIT_INACTIVE,
- UNIT_FAILED,
- UNIT_ACTIVATING,
- UNIT_DEACTIVATING,
- _UNIT_ACTIVE_STATE_MAX,
- _UNIT_ACTIVE_STATE_INVALID = -1
-};
-
-static inline bool UNIT_IS_ACTIVE_OR_RELOADING(UnitActiveState t) {
- return t == UNIT_ACTIVE || t == UNIT_RELOADING;
-}
-
-static inline bool UNIT_IS_ACTIVE_OR_ACTIVATING(UnitActiveState t) {
- return t == UNIT_ACTIVE || t == UNIT_ACTIVATING || t == UNIT_RELOADING;
-}
-
-static inline bool UNIT_IS_INACTIVE_OR_DEACTIVATING(UnitActiveState t) {
- return t == UNIT_INACTIVE || t == UNIT_FAILED || t == UNIT_DEACTIVATING;
-}
-
-static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
- return t == UNIT_INACTIVE || t == UNIT_FAILED;
-}
-
-enum UnitDependency {
- /* Positive dependencies */
- UNIT_REQUIRES,
- UNIT_REQUIRES_OVERRIDABLE,
- UNIT_REQUISITE,
- UNIT_REQUISITE_OVERRIDABLE,
- UNIT_WANTS,
- UNIT_BIND_TO,
-
- /* Inverse of the above */
- UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
- UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
- UNIT_WANTED_BY, /* inverse of 'wants' */
- UNIT_BOUND_BY, /* inverse of 'bind_to' */
-
- /* Negative dependencies */
- UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
- UNIT_CONFLICTED_BY,
-
- /* Order */
- UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
- UNIT_AFTER,
-
- /* On Failure */
- UNIT_ON_FAILURE,
-
- /* Triggers (i.e. a socket triggers a service) */
- UNIT_TRIGGERS,
- UNIT_TRIGGERED_BY,
-
- /* Propagate reloads */
- UNIT_PROPAGATE_RELOAD_TO,
- UNIT_PROPAGATE_RELOAD_FROM,
-
- /* Reference information for GC logic */
- UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
- UNIT_REFERENCED_BY,
-
- _UNIT_DEPENDENCY_MAX,
- _UNIT_DEPENDENCY_INVALID = -1
-};
-
-#include "manager.h"
-#include "job.h"
-#include "cgroup.h"
-#include "cgroup-attr.h"
-
-struct Unit {
- Manager *manager;
-
- UnitType type;
- UnitLoadState load_state;
- Unit *merged_into;
-
- char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
- char *instance;
-
- Set *names;
- Set *dependencies[_UNIT_DEPENDENCY_MAX];
-
- char *description;
-
- char *fragment_path; /* if loaded from a config file this is the primary path to it */
- usec_t fragment_mtime;
-
- /* If there is something to do with this unit, then this is
- * the job for it */
- Job *job;
-
- usec_t job_timeout;
-
- /* References to this */
- LIST_HEAD(UnitRef, refs);
-
- /* Conditions to check */
- LIST_HEAD(Condition, conditions);
-
- dual_timestamp condition_timestamp;
-
- dual_timestamp inactive_exit_timestamp;
- dual_timestamp active_enter_timestamp;
- dual_timestamp active_exit_timestamp;
- dual_timestamp inactive_enter_timestamp;
-
- /* Counterparts in the cgroup filesystem */
- CGroupBonding *cgroup_bondings;
- CGroupAttribute *cgroup_attributes;
-
- /* Per type list */
- LIST_FIELDS(Unit, units_by_type);
-
- /* Load queue */
- LIST_FIELDS(Unit, load_queue);
-
- /* D-Bus queue */
- LIST_FIELDS(Unit, dbus_queue);
-
- /* Cleanup queue */
- LIST_FIELDS(Unit, cleanup_queue);
-
- /* GC queue */
- LIST_FIELDS(Unit, gc_queue);
-
- /* Used during GC sweeps */
- unsigned gc_marker;
-
- /* When deserializing, temporarily store the job type for this
- * unit here, if there was a job scheduled */
- int deserialized_job; /* This is actually of type JobType */
-
- /* Error code when we didn't manage to load the unit (negative) */
- int load_error;
-
- /* Cached unit file state */
- UnitFileState unit_file_state;
-
- /* Garbage collect us we nobody wants or requires us anymore */
- bool stop_when_unneeded;
-
- /* Create default dependencies */
- bool default_dependencies;
-
- /* Refuse manual starting, allow starting only indirectly via dependency. */
- bool refuse_manual_start;
-
- /* Don't allow the user to stop this unit manually, allow stopping only indirectly via dependency. */
- bool refuse_manual_stop;
-
- /* Allow isolation requests */
- bool allow_isolate;
-
- /* Isolate OnFailure unit */
- bool on_failure_isolate;
-
- /* Ignore this unit when isolating */
- bool ignore_on_isolate;
-
- /* Ignore this unit when snapshotting */
- bool ignore_on_snapshot;
-
- /* Did the last condition check suceed? */
- bool condition_result;
-
- bool in_load_queue:1;
- bool in_dbus_queue:1;
- bool in_cleanup_queue:1;
- bool in_gc_queue:1;
-
- bool sent_dbus_new_signal:1;
-
- bool no_gc:1;
-
- bool in_audit:1;
-};
-
-struct UnitRef {
- /* Keeps tracks of references to a unit. This is useful so
- * that we can merge two units if necessary and correct all
- * references to them */
-
- Unit* unit;
- LIST_FIELDS(UnitRef, refs);
-};
-
-#include "service.h"
-#include "timer.h"
-#include "socket.h"
-#include "target.h"
-#include "device.h"
-#include "mount.h"
-#include "automount.h"
-#include "snapshot.h"
-#include "swap.h"
-#include "path.h"
-
-struct UnitVTable {
- const char *suffix;
-
- /* How much memory does an object of this unit type need */
- size_t object_size;
-
- /* Config file sections this unit type understands, separated
- * by NUL chars */
- const char *sections;
-
- /* This should reset all type-specific variables. This should
- * not allocate memory, and is called with zero-initialized
- * data. It should hence only initialize variables that need
- * to be set != 0. */
- void (*init)(Unit *u);
-
- /* This should free all type-specific variables. It should be
- * idempotent. */
- void (*done)(Unit *u);
-
- /* Actually load data from disk. This may fail, and should set
- * load_state to UNIT_LOADED, UNIT_MERGED or leave it at
- * UNIT_STUB if no configuration could be found. */
- int (*load)(Unit *u);
-
- /* If a a lot of units got created via enumerate(), this is
- * where to actually set the state and call unit_notify(). */
- int (*coldplug)(Unit *u);
-
- void (*dump)(Unit *u, FILE *f, const char *prefix);
-
- int (*start)(Unit *u);
- int (*stop)(Unit *u);
- int (*reload)(Unit *u);
-
- int (*kill)(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
-
- bool (*can_reload)(Unit *u);
-
- /* Write all data that cannot be restored from other sources
- * away using unit_serialize_item() */
- int (*serialize)(Unit *u, FILE *f, FDSet *fds);
-
- /* Restore one item from the serialization */
- int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
-
- /* Boils down the more complex internal state of this unit to
- * a simpler one that the engine can understand */
- UnitActiveState (*active_state)(Unit *u);
-
- /* Returns the substate specific to this unit type as
- * string. This is purely information so that we can give the
- * user a more fine grained explanation in which actual state a
- * unit is in. */
- const char* (*sub_state_to_string)(Unit *u);
-
- /* Return true when there is reason to keep this entry around
- * even nothing references it and it isn't active in any
- * way */
- bool (*check_gc)(Unit *u);
-
- /* Return true when this unit is suitable for snapshotting */
- bool (*check_snapshot)(Unit *u);
-
- void (*fd_event)(Unit *u, int fd, uint32_t events, Watch *w);
- void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
- void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
-
- /* Check whether unit needs a daemon reload */
- bool (*need_daemon_reload)(Unit *u);
-
- /* Reset failed state if we are in failed state */
- void (*reset_failed)(Unit *u);
-
- /* Called whenever any of the cgroups this unit watches for
- * ran empty */
- void (*cgroup_notify_empty)(Unit *u);
-
- /* Called whenever a process of this unit sends us a message */
- void (*notify_message)(Unit *u, pid_t pid, char **tags);
-
- /* Called whenever a name thus Unit registered for comes or
- * goes away. */
- void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
-
- /* Called whenever a bus PID lookup finishes */
- void (*bus_query_pid_done)(Unit *u, const char *name, pid_t pid);
-
- /* Called for each message received on the bus */
- DBusHandlerResult (*bus_message_handler)(Unit *u, DBusConnection *c, DBusMessage *message);
-
- /* Return the unit this unit is following */
- Unit *(*following)(Unit *u);
-
- /* Return the set of units that are following each other */
- int (*following_set)(Unit *u, Set **s);
-
- /* This is called for each unit type and should be used to
- * enumerate existing devices and load them. However,
- * everything that is loaded here should still stay in
- * inactive state. It is the job of the coldplug() call above
- * to put the units into the initial state. */
- int (*enumerate)(Manager *m);
-
- /* Type specific cleanups. */
- void (*shutdown)(Manager *m);
-
- /* When sending out PropertiesChanged signal, which properties
- * shall be invalidated? This is a NUL separated list of
- * strings, to minimize relocations a little. */
- const char *bus_invalidating_properties;
-
- /* The interface name */
- const char *bus_interface;
-
- /* Can units of this type have multiple names? */
- bool no_alias:1;
-
- /* Instances make no sense for this type */
- bool no_instances:1;
-
- /* Exclude from automatic gc */
- bool no_gc:1;
-
- /* Show status updates on the console */
- bool show_status:1;
-};
-
-extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
-
-#define UNIT_VTABLE(u) unit_vtable[(u)->type]
-
-/* For casting a unit into the various unit types */
-#define DEFINE_CAST(UPPERCASE, MixedCase) \
- static inline MixedCase* UPPERCASE(Unit *u) { \
- if (_unlikely_(!u || u->type != UNIT_##UPPERCASE)) \
- return NULL; \
- \
- return (MixedCase*) u; \
- }
-
-/* For casting the various unit types into a unit */
-#define UNIT(u) (&(u)->meta)
-
-DEFINE_CAST(SOCKET, Socket);
-DEFINE_CAST(TIMER, Timer);
-DEFINE_CAST(SERVICE, Service);
-DEFINE_CAST(TARGET, Target);
-DEFINE_CAST(DEVICE, Device);
-DEFINE_CAST(MOUNT, Mount);
-DEFINE_CAST(AUTOMOUNT, Automount);
-DEFINE_CAST(SNAPSHOT, Snapshot);
-DEFINE_CAST(SWAP, Swap);
-DEFINE_CAST(PATH, Path);
-
-Unit *unit_new(Manager *m, size_t size);
-void unit_free(Unit *u);
-
-int unit_add_name(Unit *u, const char *name);
-
-int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference);
-int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference);
-
-int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
-int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-
-int unit_add_exec_dependencies(Unit *u, ExecContext *c);
-
-int unit_add_cgroup(Unit *u, CGroupBonding *b);
-int unit_add_cgroup_from_text(Unit *u, const char *name);
-int unit_add_default_cgroups(Unit *u);
-CGroupBonding* unit_get_default_cgroup(Unit *u);
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
-
-int unit_choose_id(Unit *u, const char *name);
-int unit_set_description(Unit *u, const char *description);
-
-bool unit_check_gc(Unit *u);
-
-void unit_add_to_load_queue(Unit *u);
-void unit_add_to_dbus_queue(Unit *u);
-void unit_add_to_cleanup_queue(Unit *u);
-void unit_add_to_gc_queue(Unit *u);
-
-int unit_merge(Unit *u, Unit *other);
-int unit_merge_by_name(Unit *u, const char *other);
-
-Unit *unit_follow_merge(Unit *u);
-
-int unit_load_fragment_and_dropin(Unit *u);
-int unit_load_fragment_and_dropin_optional(Unit *u);
-int unit_load(Unit *unit);
-
-const char *unit_description(Unit *u);
-
-bool unit_has_name(Unit *u, const char *name);
-
-UnitActiveState unit_active_state(Unit *u);
-
-const char* unit_sub_state_to_string(Unit *u);
-
-void unit_dump(Unit *u, FILE *f, const char *prefix);
-
-bool unit_can_reload(Unit *u);
-bool unit_can_start(Unit *u);
-bool unit_can_isolate(Unit *u);
-
-int unit_start(Unit *u);
-int unit_stop(Unit *u);
-int unit_reload(Unit *u);
-
-int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
-
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
-
-int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w);
-void unit_unwatch_fd(Unit *u, Watch *w);
-
-int unit_watch_pid(Unit *u, pid_t pid);
-void unit_unwatch_pid(Unit *u, pid_t pid);
-
-int unit_watch_timer(Unit *u, usec_t delay, Watch *w);
-void unit_unwatch_timer(Unit *u, Watch *w);
-
-int unit_watch_bus_name(Unit *u, const char *name);
-void unit_unwatch_bus_name(Unit *u, const char *name);
-
-bool unit_job_is_applicable(Unit *u, JobType j);
-
-int set_unit_path(const char *p);
-
-char *unit_dbus_path(Unit *u);
-
-int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
-int unit_get_related_unit(Unit *u, const char *type, Unit **_found);
-
-char *unit_name_printf(Unit *u, const char* text);
-char *unit_full_printf(Unit *u, const char *text);
-char **unit_full_printf_strv(Unit *u, char **l);
-
-bool unit_can_serialize(Unit *u);
-int unit_serialize(Unit *u, FILE *f, FDSet *fds);
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5);
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
-int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
-
-int unit_add_node_link(Unit *u, const char *what, bool wants);
-
-int unit_coldplug(Unit *u);
-
-void unit_status_printf(Unit *u, const char *status, const char *format, ...);
-
-bool unit_need_daemon_reload(Unit *u);
-
-void unit_reset_failed(Unit *u);
-
-Unit *unit_following(Unit *u);
-
-bool unit_pending_inactive(Unit *u);
-bool unit_pending_active(Unit *u);
-
-int unit_add_default_target_dependency(Unit *u, Unit *target);
-
-int unit_following_set(Unit *u, Set **s);
-
-UnitType unit_name_to_type(const char *n);
-bool unit_name_is_valid(const char *n, bool template_ok);
-
-void unit_trigger_on_failure(Unit *u);
-
-bool unit_condition_test(Unit *u);
-
-UnitFileState unit_get_unit_file_state(Unit *u);
-
-Unit* unit_ref_set(UnitRef *ref, Unit *u);
-void unit_ref_unset(UnitRef *ref);
-
-#define UNIT_DEREF(ref) ((ref).unit)
-
-const char *unit_load_state_to_string(UnitLoadState i);
-UnitLoadState unit_load_state_from_string(const char *s);
-
-const char *unit_active_state_to_string(UnitActiveState i);
-UnitActiveState unit_active_state_from_string(const char *s);
-
-const char *unit_dependency_to_string(UnitDependency i);
-UnitDependency unit_dependency_from_string(const char *s);
-
-#endif
More information about the systemd-commits
mailing list