[systemd-commits] Makefile.am src/ac-power src/ac-power.c src/ask-password src/ask-password.c src/cgls src/cgls.c src/cgroups-agent src/cgroups-agent.c src/cgtop src/cgtop.c src/detect-virt src/detect-virt.c src/fsck src/fsck.c src/getty-generator src/getty-generator.c src/initctl src/initctl.c src/modules-load src/modules-load.c src/notify src/notify.c src/nspawn src/nspawn.c src/quotacheck src/quotacheck.c src/random-seed src/random-seed.c src/rc-local-generator src/rc-local-generator.c src/remount-api-vfs src/remount-api-vfs.c src/reply-password src/reply-password.c src/shutdownd src/shutdownd.c src/sysctl src/sysctl.c src/systemctl src/systemctl.c src/timestamp src/timestamp.c src/tmpfiles src/tmpfiles.c src/tty-ask-password-agent src/tty-ask-password-agent.c src/update-utmp src/update-utmp.c
Kay Sievers
kay at kemper.freedesktop.org
Thu Apr 12 08:54:54 PDT 2012
Makefile.am | 48
src/ac-power.c | 111
src/ac-power/ac-power.c | 111
src/ask-password.c | 184
src/ask-password/ask-password.c | 184
src/cgls.c | 164
src/cgls/cgls.c | 164
src/cgroups-agent.c | 101
src/cgroups-agent/cgroups-agent.c | 101
src/cgtop.c | 729 --
src/cgtop/cgtop.c | 729 ++
src/detect-virt.c | 171
src/detect-virt/detect-virt.c | 171
src/fsck.c | 406 -
src/fsck/fsck.c | 406 +
src/getty-generator.c | 183
src/getty-generator/getty-generator.c | 183
src/initctl.c | 451 -
src/initctl/initctl.c | 451 +
src/modules-load.c | 155
src/modules-load/modules-load.c | 155
src/notify.c | 228
src/notify/notify.c | 228
src/nspawn.c | 925 ---
src/nspawn/nspawn.c | 925 +++
src/quotacheck.c | 120
src/quotacheck/quotacheck.c | 120
src/random-seed.c | 148
src/random-seed/random-seed.c | 148
src/rc-local-generator.c | 108
src/rc-local-generator/rc-local-generator.c | 108
src/remount-api-vfs.c | 161
src/remount-api-vfs/remount-api-vfs.c | 161
src/reply-password.c | 109
src/reply-password/reply-password.c | 109
src/shutdownd.c | 476 -
src/shutdownd/shutdownd.c | 476 +
src/sysctl.c | 272
src/sysctl/sysctl.c | 272
src/systemctl.c | 5523 --------------------
src/systemctl/systemctl.c | 5523 ++++++++++++++++++++
src/timestamp.c | 39
src/timestamp/timestamp.c | 39
src/tmpfiles.c | 1315 ----
src/tmpfiles/tmpfiles.c | 1315 ++++
src/tty-ask-password-agent.c | 754 --
src/tty-ask-password-agent/tty-ask-password-agent.c | 754 ++
src/update-utmp.c | 423 -
src/update-utmp/update-utmp.c | 423 +
49 files changed, 13280 insertions(+), 13280 deletions(-)
New commits:
commit dce818b390a857a11f7dd634684500675cf79833
Author: Kay Sievers <kay at vrfy.org>
Date: Thu Apr 12 17:15:18 2012 +0200
move all tools to subdirs
diff --git a/Makefile.am b/Makefile.am
index edf92e3..ec7507f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -954,7 +954,7 @@ test_watchdog_LDADD = \
# ------------------------------------------------------------------------------
systemd_initctl_SOURCES = \
- src/initctl.c
+ src/initctl/initctl.c
systemd_initctl_CFLAGS = \
$(AM_CFLAGS) \
@@ -968,7 +968,7 @@ systemd_initctl_LDADD = \
# ------------------------------------------------------------------------------
systemd_update_utmp_SOURCES = \
- src/update-utmp.c
+ src/update-utmp/update-utmp.c
systemd_update_utmp_CFLAGS = \
$(AM_CFLAGS) \
@@ -983,7 +983,7 @@ systemd_update_utmp_LDADD = \
# ------------------------------------------------------------------------------
systemd_shutdownd_SOURCES = \
- src/shutdownd.c
+ src/shutdownd/shutdownd.c
systemd_shutdownd_LDADD = \
libsystemd-label.la \
@@ -1006,7 +1006,7 @@ systemd_shutdown_LDADD = \
# ------------------------------------------------------------------------------
systemd_modules_load_SOURCES = \
- src/modules-load.c
+ src/modules-load/modules-load.c
systemd_modules_load_CFLAGS = \
$(AM_CFLAGS) \
@@ -1018,7 +1018,7 @@ systemd_modules_load_LDADD = \
# ------------------------------------------------------------------------------
systemd_tmpfiles_SOURCES = \
- src/tmpfiles.c
+ src/tmpfiles/tmpfiles.c
systemd_tmpfiles_LDADD = \
libsystemd-label.la \
@@ -1037,14 +1037,14 @@ systemd_machine_id_setup_LDADD = \
# ------------------------------------------------------------------------------
systemd_sysctl_SOURCES = \
- src/sysctl.c
+ src/sysctl/sysctl.c
systemd_sysctl_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_fsck_SOURCES = \
- src/fsck.c
+ src/fsck/fsck.c
systemd_fsck_CFLAGS = \
$(AM_CFLAGS) \
@@ -1058,14 +1058,14 @@ systemd_fsck_LDADD = \
# ------------------------------------------------------------------------------
systemd_timestamp_SOURCES = \
- src/timestamp.c
+ src/timestamp/timestamp.c
systemd_timestamp_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_ac_power_SOURCES = \
- src/ac-power.c
+ src/ac-power/ac-power.c
systemd_ac_power_LDADD = \
libsystemd-shared.la \
@@ -1073,14 +1073,14 @@ systemd_ac_power_LDADD = \
# ------------------------------------------------------------------------------
systemd_detect_virt_SOURCES = \
- src/detect-virt.c
+ src/detect-virt/detect-virt.c
systemd_detect_virt_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_getty_generator_SOURCES = \
- src/getty-generator.c
+ src/getty-generator/getty-generator.c
systemd_getty_generator_LDADD = \
libsystemd-label.la \
@@ -1088,7 +1088,7 @@ systemd_getty_generator_LDADD = \
# ------------------------------------------------------------------------------
systemd_rc_local_generator_SOURCES = \
- src/rc-local-generator.c
+ src/rc-local-generator/rc-local-generator.c
systemd_rc_local_generator_LDADD = \
libsystemd-label.la \
@@ -1096,14 +1096,14 @@ systemd_rc_local_generator_LDADD = \
# ------------------------------------------------------------------------------
systemd_remount_api_vfs_SOURCES = \
- src/remount-api-vfs.c
+ src/remount-api-vfs/remount-api-vfs.c
systemd_remount_api_vfs_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_cgroups_agent_SOURCES = \
- src/cgroups-agent.c
+ src/cgroups-agent/cgroups-agent.c
systemd_cgroups_agent_CFLAGS = \
$(AM_CFLAGS) \
@@ -1116,7 +1116,7 @@ systemd_cgroups_agent_LDADD = \
# ------------------------------------------------------------------------------
systemctl_SOURCES = \
- src/systemctl.c
+ src/systemctl/systemctl.c
systemctl_CFLAGS = \
$(AM_CFLAGS) \
@@ -1134,7 +1134,7 @@ systemctl_LDADD = \
# ------------------------------------------------------------------------------
systemd_notify_SOURCES = \
- src/notify.c \
+ src/notify/notify.c \
src/readahead/sd-readahead.c
systemd_notify_LDADD = \
@@ -1143,7 +1143,7 @@ systemd_notify_LDADD = \
# ------------------------------------------------------------------------------
systemd_ask_password_SOURCES = \
- src/ask-password.c
+ src/ask-password/ask-password.c
systemd_ask_password_LDADD = \
libsystemd-label.la \
@@ -1151,28 +1151,28 @@ systemd_ask_password_LDADD = \
# ------------------------------------------------------------------------------
systemd_reply_password_SOURCES = \
- src/reply-password.c
+ src/reply-password/reply-password.c
systemd_reply_password_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_cgls_SOURCES = \
- src/cgls.c
+ src/cgls/cgls.c
systemd_cgls_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_cgtop_SOURCES = \
- src/cgtop.c
+ src/cgtop/cgtop.c
systemd_cgtop_LDADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
systemd_nspawn_SOURCES = \
- src/nspawn.c
+ src/nspawn/nspawn.c
systemd_nspawn_LDADD = \
libsystemd-label.la \
@@ -1189,7 +1189,7 @@ systemd_stdio_bridge_LDADD = \
# ------------------------------------------------------------------------------
systemd_tty_ask_password_agent_SOURCES = \
- src/tty-ask-password-agent.c
+ src/tty-ask-password-agent/tty-ask-password-agent.c
systemd_tty_ask_password_agent_LDADD = \
libsystemd-label.la \
@@ -2306,7 +2306,7 @@ EXTRA_DIST += \
units/quotacheck.service.in
systemd_quotacheck_SOURCES = \
- src/quotacheck.c
+ src/quotacheck/quotacheck.c
systemd_quotacheck_LDADD = \
libsystemd-shared.la
@@ -2326,7 +2326,7 @@ EXTRA_DIST += \
units/systemd-random-seed-load.service.in
systemd_random_seed_SOURCES = \
- src/random-seed.c
+ src/random-seed/random-seed.c
systemd_random_seed_LDADD = \
libsystemd-label.la \
diff --git a/src/ac-power.c b/src/ac-power.c
deleted file mode 100644
index 37313cf..0000000
--- a/src/ac-power.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
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <libudev.h>
-
-#include "util.h"
-
-static int on_ac_power(void) {
- int r;
-
- struct udev *udev;
- struct udev_enumerate *e = NULL;
- struct udev_list_entry *item = NULL, *first = NULL;
- bool found_offline = false, found_online = false;
-
- if (!(udev = udev_new())) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(e = udev_enumerate_new(udev))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (udev_enumerate_add_match_subsystem(e, "power_supply") < 0) {
- r = -EIO;
- goto finish;
- }
-
- if (udev_enumerate_scan_devices(e) < 0) {
- r = -EIO;
- goto finish;
- }
-
- first = udev_enumerate_get_list_entry(e);
- udev_list_entry_foreach(item, first) {
- struct udev_device *d;
- const char *type, *online;
-
- if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(type = udev_device_get_sysattr_value(d, "type")))
- goto next;
-
- if (!streq(type, "Mains"))
- goto next;
-
- if (!(online = udev_device_get_sysattr_value(d, "online")))
- goto next;
-
- if (streq(online, "1")) {
- found_online = true;
- break;
- } else if (streq(online, "0"))
- found_offline = true;
-
- next:
- udev_device_unref(d);
- }
-
- r = found_online || !found_offline;
-
-finish:
- if (e)
- udev_enumerate_unref(e);
-
- if (udev)
- udev_unref(udev);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- int r;
-
- /* This is mostly intended to be used for scripts which want
- * to detect whether AC power is plugged in or not. */
-
- if ((r = on_ac_power()) < 0) {
- log_error("Failed to read AC status: %s", strerror(-r));
- return EXIT_FAILURE;
- }
-
- return r == 0;
-}
diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c
new file mode 100644
index 0000000..37313cf
--- /dev/null
+++ b/src/ac-power/ac-power.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
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <libudev.h>
+
+#include "util.h"
+
+static int on_ac_power(void) {
+ int r;
+
+ struct udev *udev;
+ struct udev_enumerate *e = NULL;
+ struct udev_list_entry *item = NULL, *first = NULL;
+ bool found_offline = false, found_online = false;
+
+ if (!(udev = udev_new())) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(e = udev_enumerate_new(udev))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (udev_enumerate_add_match_subsystem(e, "power_supply") < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (udev_enumerate_scan_devices(e) < 0) {
+ r = -EIO;
+ goto finish;
+ }
+
+ first = udev_enumerate_get_list_entry(e);
+ udev_list_entry_foreach(item, first) {
+ struct udev_device *d;
+ const char *type, *online;
+
+ if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(type = udev_device_get_sysattr_value(d, "type")))
+ goto next;
+
+ if (!streq(type, "Mains"))
+ goto next;
+
+ if (!(online = udev_device_get_sysattr_value(d, "online")))
+ goto next;
+
+ if (streq(online, "1")) {
+ found_online = true;
+ break;
+ } else if (streq(online, "0"))
+ found_offline = true;
+
+ next:
+ udev_device_unref(d);
+ }
+
+ r = found_online || !found_offline;
+
+finish:
+ if (e)
+ udev_enumerate_unref(e);
+
+ if (udev)
+ udev_unref(udev);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ /* This is mostly intended to be used for scripts which want
+ * to detect whether AC power is plugged in or not. */
+
+ if ((r = on_ac_power()) < 0) {
+ log_error("Failed to read AC status: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ return r == 0;
+}
diff --git a/src/ask-password.c b/src/ask-password.c
deleted file mode 100644
index 5f67570..0000000
--- a/src/ask-password.c
+++ /dev/null
@@ -1,184 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
-#include <getopt.h>
-#include <termios.h>
-#include <limits.h>
-#include <stddef.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "strv.h"
-#include "ask-password-api.h"
-#include "def.h"
-
-static const char *arg_icon = NULL;
-static const char *arg_message = NULL;
-static bool arg_use_tty = true;
-static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
-static bool arg_accept_cached = false;
-static bool arg_multiple = false;
-
-static int help(void) {
-
- printf("%s [OPTIONS...] MESSAGE\n\n"
- "Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
- " -h --help Show this help\n"
- " --icon=NAME Icon name\n"
- " --timeout=SEC Timeout in sec\n"
- " --no-tty Ask question via agent even on TTY\n"
- " --accept-cached Accept cached passwords\n"
- " --multiple List multiple passwords if available\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_ICON = 0x100,
- ARG_TIMEOUT,
- ARG_NO_TTY,
- ARG_ACCEPT_CACHED,
- ARG_MULTIPLE
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "icon", required_argument, NULL, ARG_ICON },
- { "timeout", required_argument, NULL, ARG_TIMEOUT },
- { "no-tty", no_argument, NULL, ARG_NO_TTY },
- { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
- { "multiple", no_argument, NULL, ARG_MULTIPLE },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_ICON:
- arg_icon = optarg;
- break;
-
- case ARG_TIMEOUT:
- if (parse_usec(optarg, &arg_timeout) < 0) {
- log_error("Failed to parse --timeout parameter %s", optarg);
- return -EINVAL;
- }
- break;
-
- case ARG_NO_TTY:
- arg_use_tty = false;
- break;
-
- case ARG_ACCEPT_CACHED:
- arg_accept_cached = true;
- break;
-
- case ARG_MULTIPLE:
- arg_multiple = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind != argc-1) {
- help();
- return -EINVAL;
- }
-
- arg_message = argv[optind];
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r;
- usec_t timeout;
-
- log_parse_environment();
- log_open();
-
- if ((r = parse_argv(argc, argv)) <= 0)
- goto finish;
-
- if (arg_timeout > 0)
- timeout = now(CLOCK_MONOTONIC) + arg_timeout;
- else
- timeout = 0;
-
- if (arg_use_tty && isatty(STDIN_FILENO)) {
- char *password = NULL;
-
- if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) {
- puts(password);
- free(password);
- }
-
- } else {
- char **l;
-
- if ((r = ask_password_agent(arg_message, arg_icon, timeout, arg_accept_cached, &l)) >= 0) {
- char **p;
-
- STRV_FOREACH(p, l) {
- puts(*p);
-
- if (!arg_multiple)
- break;
- }
-
- strv_free(l);
- }
- }
-
-finish:
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
new file mode 100644
index 0000000..5f67570
--- /dev/null
+++ b/src/ask-password/ask-password.c
@@ -0,0 +1,184 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <getopt.h>
+#include <termios.h>
+#include <limits.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "strv.h"
+#include "ask-password-api.h"
+#include "def.h"
+
+static const char *arg_icon = NULL;
+static const char *arg_message = NULL;
+static bool arg_use_tty = true;
+static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
+static bool arg_accept_cached = false;
+static bool arg_multiple = false;
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] MESSAGE\n\n"
+ "Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
+ " -h --help Show this help\n"
+ " --icon=NAME Icon name\n"
+ " --timeout=SEC Timeout in sec\n"
+ " --no-tty Ask question via agent even on TTY\n"
+ " --accept-cached Accept cached passwords\n"
+ " --multiple List multiple passwords if available\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_ICON = 0x100,
+ ARG_TIMEOUT,
+ ARG_NO_TTY,
+ ARG_ACCEPT_CACHED,
+ ARG_MULTIPLE
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "icon", required_argument, NULL, ARG_ICON },
+ { "timeout", required_argument, NULL, ARG_TIMEOUT },
+ { "no-tty", no_argument, NULL, ARG_NO_TTY },
+ { "accept-cached", no_argument, NULL, ARG_ACCEPT_CACHED },
+ { "multiple", no_argument, NULL, ARG_MULTIPLE },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_ICON:
+ arg_icon = optarg;
+ break;
+
+ case ARG_TIMEOUT:
+ if (parse_usec(optarg, &arg_timeout) < 0) {
+ log_error("Failed to parse --timeout parameter %s", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case ARG_NO_TTY:
+ arg_use_tty = false;
+ break;
+
+ case ARG_ACCEPT_CACHED:
+ arg_accept_cached = true;
+ break;
+
+ case ARG_MULTIPLE:
+ arg_multiple = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind != argc-1) {
+ help();
+ return -EINVAL;
+ }
+
+ arg_message = argv[optind];
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ usec_t timeout;
+
+ log_parse_environment();
+ log_open();
+
+ if ((r = parse_argv(argc, argv)) <= 0)
+ goto finish;
+
+ if (arg_timeout > 0)
+ timeout = now(CLOCK_MONOTONIC) + arg_timeout;
+ else
+ timeout = 0;
+
+ if (arg_use_tty && isatty(STDIN_FILENO)) {
+ char *password = NULL;
+
+ if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) {
+ puts(password);
+ free(password);
+ }
+
+ } else {
+ char **l;
+
+ if ((r = ask_password_agent(arg_message, arg_icon, timeout, arg_accept_cached, &l)) >= 0) {
+ char **p;
+
+ STRV_FOREACH(p, l) {
+ puts(*p);
+
+ if (!arg_multiple)
+ break;
+ }
+
+ strv_free(l);
+ }
+ }
+
+finish:
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/cgls.c b/src/cgls.c
deleted file mode 100644
index fd02d52..0000000
--- a/src/cgls.c
+++ /dev/null
@@ -1,164 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <limits.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <getopt.h>
-#include <string.h>
-
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "log.h"
-#include "util.h"
-#include "pager.h"
-
-static bool arg_no_pager = false;
-static bool arg_kernel_threads = false;
-
-static void help(void) {
-
- printf("%s [OPTIONS...] [CGROUP...]\n\n"
- "Recursively show control group contents.\n\n"
- " -h --help Show this help\n"
- " --no-pager Do not pipe output into a pager\n"
- " -k Include kernel threads in output\n",
- program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_NO_PAGER = 0x100
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 1);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "hk", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_NO_PAGER:
- arg_no_pager = true;
- break;
-
- case 'k':
- arg_kernel_threads = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r = 0, retval = EXIT_FAILURE;
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r < 0)
- goto finish;
- else if (r == 0) {
- retval = EXIT_SUCCESS;
- goto finish;
- }
-
- if (!arg_no_pager)
- pager_open();
-
- if (optind < argc) {
- unsigned i;
-
- for (i = (unsigned) optind; i < (unsigned) argc; i++) {
- int q;
- printf("%s:\n", argv[i]);
-
- q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads);
- if (q < 0)
- r = q;
- }
-
- } else {
- char *p;
-
- p = get_current_dir_name();
- if (!p) {
- log_error("Cannot determine current working directory: %m");
- goto finish;
- }
-
- if (path_startswith(p, "/sys/fs/cgroup")) {
- printf("Working Directory %s:\n", p);
- r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads);
- } else {
- char *root = NULL;
- const char *t = NULL;
-
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
- if (r < 0)
- t = "/";
- else {
- if (endswith(root, "/system"))
- root[strlen(root)-7] = 0;
-
- t = root[0] ? root : "/";
- }
-
- r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads);
- free(root);
- }
-
- free(p);
- }
-
- if (r < 0)
- log_error("Failed to list cgroup tree: %s", strerror(-r));
-
- retval = EXIT_SUCCESS;
-
-finish:
- pager_close();
-
- return retval;
-}
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
new file mode 100644
index 0000000..fd02d52
--- /dev/null
+++ b/src/cgls/cgls.c
@@ -0,0 +1,164 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+
+#include "cgroup-show.h"
+#include "cgroup-util.h"
+#include "log.h"
+#include "util.h"
+#include "pager.h"
+
+static bool arg_no_pager = false;
+static bool arg_kernel_threads = false;
+
+static void help(void) {
+
+ printf("%s [OPTIONS...] [CGROUP...]\n\n"
+ "Recursively show control group contents.\n\n"
+ " -h --help Show this help\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " -k Include kernel threads in output\n",
+ program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_NO_PAGER = 0x100
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 1);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hk", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case 'k':
+ arg_kernel_threads = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r = 0, retval = EXIT_FAILURE;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ goto finish;
+ else if (r == 0) {
+ retval = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ if (!arg_no_pager)
+ pager_open();
+
+ if (optind < argc) {
+ unsigned i;
+
+ for (i = (unsigned) optind; i < (unsigned) argc; i++) {
+ int q;
+ printf("%s:\n", argv[i]);
+
+ q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads);
+ if (q < 0)
+ r = q;
+ }
+
+ } else {
+ char *p;
+
+ p = get_current_dir_name();
+ if (!p) {
+ log_error("Cannot determine current working directory: %m");
+ goto finish;
+ }
+
+ if (path_startswith(p, "/sys/fs/cgroup")) {
+ printf("Working Directory %s:\n", p);
+ r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads);
+ } else {
+ char *root = NULL;
+ const char *t = NULL;
+
+ r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
+ if (r < 0)
+ t = "/";
+ else {
+ if (endswith(root, "/system"))
+ root[strlen(root)-7] = 0;
+
+ t = root[0] ? root : "/";
+ }
+
+ r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads);
+ free(root);
+ }
+
+ free(p);
+ }
+
+ if (r < 0)
+ log_error("Failed to list cgroup tree: %s", strerror(-r));
+
+ retval = EXIT_SUCCESS;
+
+finish:
+ pager_close();
+
+ return retval;
+}
diff --git a/src/cgroups-agent.c b/src/cgroups-agent.c
deleted file mode 100644
index 7a6173e..0000000
--- a/src/cgroups-agent.c
+++ /dev/null
@@ -1,101 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <dbus/dbus.h>
-
-#include <stdlib.h>
-
-#include "log.h"
-#include "dbus-common.h"
-
-int main(int argc, char *argv[]) {
- DBusError error;
- DBusConnection *bus = NULL;
- DBusMessage *m = NULL;
- int r = EXIT_FAILURE;
-
- dbus_error_init(&error);
-
- if (argc != 2) {
- log_error("Incorrect number of arguments.");
- goto finish;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- /* We send this event to the private D-Bus socket and then the
- * system instance will forward this to the system bus. We do
- * this to avoid an activation loop when we start dbus when we
- * are called when the dbus service is shut down. */
-
- if (!(bus = dbus_connection_open_private("unix:path=/run/systemd/private", &error))) {
-#ifndef LEGACY
- dbus_error_free(&error);
-
- /* Retry with the pre v21 socket name, to ease upgrades */
- if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", &error))) {
-#endif
- log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
- goto finish;
- }
-#ifndef LEGACY
- }
-#endif
-
- if (bus_check_peercred(bus) < 0) {
- log_error("Bus owner not root.");
- goto finish;
- }
-
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
- log_error("Could not allocate signal message.");
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &argv[1],
- DBUS_TYPE_INVALID)) {
- log_error("Could not attach group information to signal message.");
- goto finish;
- }
-
- if (!dbus_connection_send(bus, m, NULL)) {
- log_error("Failed to send signal message on private connection.");
- goto finish;
- }
-
- r = EXIT_SUCCESS;
-
-finish:
- if (bus) {
- dbus_connection_flush(bus);
- dbus_connection_close(bus);
- dbus_connection_unref(bus);
- }
-
- if (m)
- dbus_message_unref(m);
-
- dbus_error_free(&error);
- return r;
-}
diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c
new file mode 100644
index 0000000..7a6173e
--- /dev/null
+++ b/src/cgroups-agent/cgroups-agent.c
@@ -0,0 +1,101 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include <stdlib.h>
+
+#include "log.h"
+#include "dbus-common.h"
+
+int main(int argc, char *argv[]) {
+ DBusError error;
+ DBusConnection *bus = NULL;
+ DBusMessage *m = NULL;
+ int r = EXIT_FAILURE;
+
+ dbus_error_init(&error);
+
+ if (argc != 2) {
+ log_error("Incorrect number of arguments.");
+ goto finish;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ /* We send this event to the private D-Bus socket and then the
+ * system instance will forward this to the system bus. We do
+ * this to avoid an activation loop when we start dbus when we
+ * are called when the dbus service is shut down. */
+
+ if (!(bus = dbus_connection_open_private("unix:path=/run/systemd/private", &error))) {
+#ifndef LEGACY
+ dbus_error_free(&error);
+
+ /* Retry with the pre v21 socket name, to ease upgrades */
+ if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", &error))) {
+#endif
+ log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+ goto finish;
+ }
+#ifndef LEGACY
+ }
+#endif
+
+ if (bus_check_peercred(bus) < 0) {
+ log_error("Bus owner not root.");
+ goto finish;
+ }
+
+ if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
+ log_error("Could not allocate signal message.");
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &argv[1],
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not attach group information to signal message.");
+ goto finish;
+ }
+
+ if (!dbus_connection_send(bus, m, NULL)) {
+ log_error("Failed to send signal message on private connection.");
+ goto finish;
+ }
+
+ r = EXIT_SUCCESS;
+
+finish:
+ if (bus) {
+ dbus_connection_flush(bus);
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ if (m)
+ dbus_message_unref(m);
+
+ dbus_error_free(&error);
+ return r;
+}
diff --git a/src/cgtop.c b/src/cgtop.c
deleted file mode 100644
index 1fe247c..0000000
--- a/src/cgtop.c
+++ /dev/null
@@ -1,729 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2012 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <alloca.h>
-#include <getopt.h>
-
-#include "util.h"
-#include "hashmap.h"
-#include "cgroup-util.h"
-
-typedef struct Group {
- char *path;
-
- bool n_tasks_valid:1;
- bool cpu_valid:1;
- bool memory_valid:1;
- bool io_valid:1;
-
- unsigned n_tasks;
-
- unsigned cpu_iteration;
- uint64_t cpu_usage;
- struct timespec cpu_timestamp;
- double cpu_fraction;
-
- uint64_t memory;
-
- unsigned io_iteration;
- uint64_t io_input, io_output;
- struct timespec io_timestamp;
- uint64_t io_input_bps, io_output_bps;
-} Group;
-
-static unsigned arg_depth = 2;
-static usec_t arg_delay = 1*USEC_PER_SEC;
-
-static enum {
- ORDER_PATH,
- ORDER_TASKS,
- ORDER_CPU,
- ORDER_MEMORY,
- ORDER_IO
-} arg_order = ORDER_CPU;
-
-static void group_free(Group *g) {
- assert(g);
-
- free(g->path);
- free(g);
-}
-
-static void group_hashmap_clear(Hashmap *h) {
- Group *g;
-
- while ((g = hashmap_steal_first(h)))
- group_free(g);
-}
-
-static void group_hashmap_free(Hashmap *h) {
- group_hashmap_clear(h);
- hashmap_free(h);
-}
-
-static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
- Group *g;
- int r;
- FILE *f;
- pid_t pid;
- unsigned n;
-
- assert(controller);
- assert(path);
- assert(a);
-
- g = hashmap_get(a, path);
- if (!g) {
- g = hashmap_get(b, path);
- if (!g) {
- g = new0(Group, 1);
- if (!g)
- return -ENOMEM;
-
- g->path = strdup(path);
- if (!g->path) {
- group_free(g);
- return -ENOMEM;
- }
-
- r = hashmap_put(a, g->path, g);
- if (r < 0) {
- group_free(g);
- return r;
- }
- } else {
- assert_se(hashmap_move_one(a, b, path) == 0);
- g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
- }
- }
-
- /* Regardless which controller, let's find the maximum number
- * of processes in any of it */
-
- r = cg_enumerate_tasks(controller, path, &f);
- if (r < 0)
- return r;
-
- n = 0;
- while (cg_read_pid(f, &pid) > 0)
- n++;
- fclose(f);
-
- if (n > 0) {
- if (g->n_tasks_valid)
- g->n_tasks = MAX(g->n_tasks, n);
- else
- g->n_tasks = n;
-
- g->n_tasks_valid = true;
- }
-
- if (streq(controller, "cpuacct")) {
- uint64_t new_usage;
- char *p, *v;
- struct timespec ts;
-
- r = cg_get_path(controller, path, "cpuacct.usage", &p);
- if (r < 0)
- return r;
-
- r = read_one_line_file(p, &v);
- free(p);
- if (r < 0)
- return r;
-
- r = safe_atou64(v, &new_usage);
- free(v);
- if (r < 0)
- return r;
-
- assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
-
- if (g->cpu_iteration == iteration - 1) {
- uint64_t x, y;
-
- x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
- ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
-
- y = new_usage - g->cpu_usage;
-
- if (y > 0) {
- g->cpu_fraction = (double) y / (double) x;
- g->cpu_valid = true;
- }
- }
-
- g->cpu_usage = new_usage;
- g->cpu_timestamp = ts;
- g->cpu_iteration = iteration;
-
- } else if (streq(controller, "memory")) {
- char *p, *v;
-
- r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
- if (r < 0)
- return r;
-
- r = read_one_line_file(p, &v);
- free(p);
- if (r < 0)
- return r;
-
- r = safe_atou64(v, &g->memory);
- free(v);
- if (r < 0)
- return r;
-
- if (g->memory > 0)
- g->memory_valid = true;
-
- } else if (streq(controller, "blkio")) {
- char *p;
- uint64_t wr = 0, rd = 0;
- struct timespec ts;
-
- r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
- if (r < 0)
- return r;
-
- f = fopen(p, "re");
- free(p);
-
- if (!f)
- return -errno;
-
- for (;;) {
- char line[LINE_MAX], *l;
- uint64_t k, *q;
-
- if (!fgets(line, sizeof(line), f))
- break;
-
- l = strstrip(line);
- l += strcspn(l, WHITESPACE);
- l += strspn(l, WHITESPACE);
-
- if (first_word(l, "Read")) {
- l += 4;
- q = &rd;
- } else if (first_word(l, "Write")) {
- l += 5;
- q = ≀
- } else
- continue;
-
- l += strspn(l, WHITESPACE);
- r = safe_atou64(l, &k);
- if (r < 0)
- continue;
-
- *q += k;
- }
-
- fclose(f);
-
- assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
-
- if (g->io_iteration == iteration - 1) {
- uint64_t x, yr, yw;
-
- x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
- ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
-
- yr = rd - g->io_input;
- yw = wr - g->io_output;
-
- if (yr > 0 || yw > 0) {
- g->io_input_bps = (yr * 1000000000ULL) / x;
- g->io_output_bps = (yw * 1000000000ULL) / x;
- g->io_valid = true;
-
- }
- }
-
- g->io_input = rd;
- g->io_output = wr;
- g->io_timestamp = ts;
- g->io_iteration = iteration;
- }
-
- return 0;
-}
-
-static int refresh_one(
- const char *controller,
- const char *path,
- Hashmap *a,
- Hashmap *b,
- unsigned iteration,
- unsigned depth) {
-
- DIR *d = NULL;
- int r;
-
- assert(controller);
- assert(path);
- assert(a);
-
- if (depth > arg_depth)
- return 0;
-
- r = process(controller, path, a, b, iteration);
- if (r < 0)
- return r;
-
- r = cg_enumerate_subgroups(controller, path, &d);
- if (r < 0) {
- if (r == ENOENT)
- return 0;
-
- return r;
- }
-
- for (;;) {
- char *fn, *p;
-
- r = cg_read_subgroup(d, &fn);
- if (r <= 0)
- goto finish;
-
- p = join(path, "/", fn, NULL);
- free(fn);
-
- if (!p) {
- r = -ENOMEM;
- goto finish;
- }
-
- path_kill_slashes(p);
-
- r = refresh_one(controller, p, a, b, iteration, depth + 1);
- free(p);
-
- if (r < 0)
- goto finish;
- }
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
- int r;
-
- assert(a);
-
- r = refresh_one("name=systemd", "/", a, b, iteration, 0);
- if (r < 0)
- return r;
-
- r = refresh_one("cpuacct", "/", a, b, iteration, 0);
- if (r < 0)
- return r;
-
- r = refresh_one("memory", "/", a, b, iteration, 0);
- if (r < 0)
- return r;
-
- return refresh_one("blkio", "/", a, b, iteration, 0);
-}
-
-static int group_compare(const void*a, const void *b) {
- const Group *x = *(Group**)a, *y = *(Group**)b;
-
- if (path_startswith(y->path, x->path))
- return -1;
- if (path_startswith(x->path, y->path))
- return 1;
-
- if (arg_order == ORDER_CPU) {
- if (x->cpu_valid && y->cpu_valid) {
-
- if (x->cpu_fraction > y->cpu_fraction)
- return -1;
- else if (x->cpu_fraction < y->cpu_fraction)
- return 1;
- } else if (x->cpu_valid)
- return -1;
- else if (y->cpu_valid)
- return 1;
- }
-
- if (arg_order == ORDER_TASKS) {
-
- if (x->n_tasks_valid && y->n_tasks_valid) {
- if (x->n_tasks > y->n_tasks)
- return -1;
- else if (x->n_tasks < y->n_tasks)
- return 1;
- } else if (x->n_tasks_valid)
- return -1;
- else if (y->n_tasks_valid)
- return 1;
- }
-
- if (arg_order == ORDER_MEMORY) {
- if (x->memory_valid && y->memory_valid) {
- if (x->memory > y->memory)
- return -1;
- else if (x->memory < y->memory)
- return 1;
- } else if (x->memory_valid)
- return -1;
- else if (y->memory_valid)
- return 1;
- }
-
- if (arg_order == ORDER_IO) {
- if (x->io_valid && y->io_valid) {
- if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
- return -1;
- else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
- return 1;
- } else if (x->io_valid)
- return -1;
- else if (y->io_valid)
- return 1;
- }
-
- return strcmp(x->path, y->path);
-}
-
-static int display(Hashmap *a) {
- Iterator i;
- Group *g;
- Group **array;
- unsigned rows, n = 0, j;
-
- assert(a);
-
- /* Set cursor to top left corner and clear screen */
- fputs("\033[H"
- "\033[2J", stdout);
-
- array = alloca(sizeof(Group*) * hashmap_size(a));
-
- HASHMAP_FOREACH(g, a, i)
- if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
- array[n++] = g;
-
- qsort(array, n, sizeof(Group*), group_compare);
-
- rows = fd_lines(STDOUT_FILENO);
- if (rows <= 0)
- rows = 25;
-
- printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
- arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", "Path", arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks", arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU", arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory", arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "",
- arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "");
-
- for (j = 0; j < n; j++) {
- char *p;
- char m[FORMAT_BYTES_MAX];
-
- if (j + 5 > rows)
- break;
-
- g = array[j];
-
- p = ellipsize(g->path, 37, 33);
- printf("%-37s", p ? p : g->path);
- free(p);
-
- if (g->n_tasks_valid)
- printf(" %7u", g->n_tasks);
- else
- fputs(" -", stdout);
-
- if (g->cpu_valid)
- printf(" %6.1f", g->cpu_fraction*100);
- else
- fputs(" -", stdout);
-
- if (g->memory_valid)
- printf(" %8s", format_bytes(m, sizeof(m), g->memory));
- else
- fputs(" -", stdout);
-
- if (g->io_valid) {
- printf(" %8s",
- format_bytes(m, sizeof(m), g->io_input_bps));
- printf(" %8s",
- format_bytes(m, sizeof(m), g->io_output_bps));
- } else
- fputs(" - -", stdout);
-
- putchar('\n');
- }
-
- return 0;
-}
-
-static void help(void) {
-
- printf("%s [OPTIONS...]\n\n"
- "Show top control groups by their resource usage.\n\n"
- " -h --help Show this help\n"
- " -p Order by path\n"
- " -t Order by number of tasks\n"
- " -c Order by CPU load\n"
- " -m Order by memory load\n"
- " -i Order by IO load\n"
- " -d --delay=DELAY Specify delay\n"
- " --depth=DEPTH Maximum traversal depth (default: 2)\n",
- program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_DEPTH = 0x100
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "delay", required_argument, NULL, 'd' },
- { "depth", required_argument, NULL, ARG_DEPTH },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
- int r;
-
- assert(argc >= 1);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_DEPTH:
- r = safe_atou(optarg, &arg_depth);
- if (r < 0) {
- log_error("Failed to parse depth parameter.");
- return -EINVAL;
- }
-
- break;
-
- case 'd':
- r = parse_usec(optarg, &arg_delay);
- if (r < 0 || arg_delay <= 0) {
- log_error("Failed to parse delay parameter.");
- return -EINVAL;
- }
-
- break;
-
- case 'p':
- arg_order = ORDER_PATH;
- break;
-
- case 't':
- arg_order = ORDER_TASKS;
- break;
-
- case 'c':
- arg_order = ORDER_CPU;
- break;
-
- case 'm':
- arg_order = ORDER_MEMORY;
- break;
-
- case 'i':
- arg_order = ORDER_IO;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r;
- Hashmap *a = NULL, *b = NULL;
- unsigned iteration = 0;
- usec_t last_refresh = 0;
- bool quit = false, immediate_refresh = false;
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- goto finish;
-
- a = hashmap_new(string_hash_func, string_compare_func);
- b = hashmap_new(string_hash_func, string_compare_func);
- if (!a || !b) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- while (!quit) {
- Hashmap *c;
- usec_t t;
- char key;
- char h[FORMAT_TIMESPAN_MAX];
-
- t = now(CLOCK_MONOTONIC);
-
- if (t >= last_refresh + arg_delay || immediate_refresh) {
-
- r = refresh(a, b, iteration++);
- if (r < 0)
- goto finish;
-
- group_hashmap_clear(b);
-
- c = a;
- a = b;
- b = c;
-
- last_refresh = t;
- immediate_refresh = false;
- }
-
- r = display(b);
- if (r < 0)
- goto finish;
-
- r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
- if (r == -ETIMEDOUT)
- continue;
- if (r < 0) {
- log_error("Couldn't read key: %s", strerror(-r));
- goto finish;
- }
-
- fputs("\r \r", stdout);
- fflush(stdout);
-
- switch (key) {
-
- case ' ':
- immediate_refresh = true;
- break;
-
- case 'q':
- quit = true;
- break;
-
- case 'p':
- arg_order = ORDER_PATH;
- break;
-
- case 't':
- arg_order = ORDER_TASKS;
- break;
-
- case 'c':
- arg_order = ORDER_CPU;
- break;
-
- case 'm':
- arg_order = ORDER_MEMORY;
- break;
-
- case 'i':
- arg_order = ORDER_IO;
- break;
-
- case '+':
- if (arg_delay < USEC_PER_SEC)
- arg_delay += USEC_PER_MSEC*250;
- else
- arg_delay += USEC_PER_SEC;
-
- fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
- fflush(stdout);
- sleep(1);
- break;
-
- case '-':
- if (arg_delay <= USEC_PER_MSEC*500)
- arg_delay = USEC_PER_MSEC*250;
- else if (arg_delay < USEC_PER_MSEC*1250)
- arg_delay -= USEC_PER_MSEC*250;
- else
- arg_delay -= USEC_PER_SEC;
-
- fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
- fflush(stdout);
- sleep(1);
- break;
-
- case '?':
- case 'h':
- fprintf(stdout,
- "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
- "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
- fflush(stdout);
- sleep(3);
- break;
-
- default:
- fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
- fflush(stdout);
- sleep(1);
- break;
- }
- }
-
- log_info("Exiting.");
-
- r = 0;
-
-finish:
- group_hashmap_free(a);
- group_hashmap_free(b);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
new file mode 100644
index 0000000..1fe247c
--- /dev/null
+++ b/src/cgtop/cgtop.c
@@ -0,0 +1,729 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "cgroup-util.h"
+
+typedef struct Group {
+ char *path;
+
+ bool n_tasks_valid:1;
+ bool cpu_valid:1;
+ bool memory_valid:1;
+ bool io_valid:1;
+
+ unsigned n_tasks;
+
+ unsigned cpu_iteration;
+ uint64_t cpu_usage;
+ struct timespec cpu_timestamp;
+ double cpu_fraction;
+
+ uint64_t memory;
+
+ unsigned io_iteration;
+ uint64_t io_input, io_output;
+ struct timespec io_timestamp;
+ uint64_t io_input_bps, io_output_bps;
+} Group;
+
+static unsigned arg_depth = 2;
+static usec_t arg_delay = 1*USEC_PER_SEC;
+
+static enum {
+ ORDER_PATH,
+ ORDER_TASKS,
+ ORDER_CPU,
+ ORDER_MEMORY,
+ ORDER_IO
+} arg_order = ORDER_CPU;
+
+static void group_free(Group *g) {
+ assert(g);
+
+ free(g->path);
+ free(g);
+}
+
+static void group_hashmap_clear(Hashmap *h) {
+ Group *g;
+
+ while ((g = hashmap_steal_first(h)))
+ group_free(g);
+}
+
+static void group_hashmap_free(Hashmap *h) {
+ group_hashmap_clear(h);
+ hashmap_free(h);
+}
+
+static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
+ Group *g;
+ int r;
+ FILE *f;
+ pid_t pid;
+ unsigned n;
+
+ assert(controller);
+ assert(path);
+ assert(a);
+
+ g = hashmap_get(a, path);
+ if (!g) {
+ g = hashmap_get(b, path);
+ if (!g) {
+ g = new0(Group, 1);
+ if (!g)
+ return -ENOMEM;
+
+ g->path = strdup(path);
+ if (!g->path) {
+ group_free(g);
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(a, g->path, g);
+ if (r < 0) {
+ group_free(g);
+ return r;
+ }
+ } else {
+ assert_se(hashmap_move_one(a, b, path) == 0);
+ g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
+ }
+ }
+
+ /* Regardless which controller, let's find the maximum number
+ * of processes in any of it */
+
+ r = cg_enumerate_tasks(controller, path, &f);
+ if (r < 0)
+ return r;
+
+ n = 0;
+ while (cg_read_pid(f, &pid) > 0)
+ n++;
+ fclose(f);
+
+ if (n > 0) {
+ if (g->n_tasks_valid)
+ g->n_tasks = MAX(g->n_tasks, n);
+ else
+ g->n_tasks = n;
+
+ g->n_tasks_valid = true;
+ }
+
+ if (streq(controller, "cpuacct")) {
+ uint64_t new_usage;
+ char *p, *v;
+ struct timespec ts;
+
+ r = cg_get_path(controller, path, "cpuacct.usage", &p);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(p, &v);
+ free(p);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &new_usage);
+ free(v);
+ if (r < 0)
+ return r;
+
+ assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
+ if (g->cpu_iteration == iteration - 1) {
+ uint64_t x, y;
+
+ x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
+ ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
+
+ y = new_usage - g->cpu_usage;
+
+ if (y > 0) {
+ g->cpu_fraction = (double) y / (double) x;
+ g->cpu_valid = true;
+ }
+ }
+
+ g->cpu_usage = new_usage;
+ g->cpu_timestamp = ts;
+ g->cpu_iteration = iteration;
+
+ } else if (streq(controller, "memory")) {
+ char *p, *v;
+
+ r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(p, &v);
+ free(p);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &g->memory);
+ free(v);
+ if (r < 0)
+ return r;
+
+ if (g->memory > 0)
+ g->memory_valid = true;
+
+ } else if (streq(controller, "blkio")) {
+ char *p;
+ uint64_t wr = 0, rd = 0;
+ struct timespec ts;
+
+ r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
+ if (r < 0)
+ return r;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ for (;;) {
+ char line[LINE_MAX], *l;
+ uint64_t k, *q;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ l = strstrip(line);
+ l += strcspn(l, WHITESPACE);
+ l += strspn(l, WHITESPACE);
+
+ if (first_word(l, "Read")) {
+ l += 4;
+ q = &rd;
+ } else if (first_word(l, "Write")) {
+ l += 5;
+ q = ≀
+ } else
+ continue;
+
+ l += strspn(l, WHITESPACE);
+ r = safe_atou64(l, &k);
+ if (r < 0)
+ continue;
+
+ *q += k;
+ }
+
+ fclose(f);
+
+ assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
+ if (g->io_iteration == iteration - 1) {
+ uint64_t x, yr, yw;
+
+ x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
+ ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
+
+ yr = rd - g->io_input;
+ yw = wr - g->io_output;
+
+ if (yr > 0 || yw > 0) {
+ g->io_input_bps = (yr * 1000000000ULL) / x;
+ g->io_output_bps = (yw * 1000000000ULL) / x;
+ g->io_valid = true;
+
+ }
+ }
+
+ g->io_input = rd;
+ g->io_output = wr;
+ g->io_timestamp = ts;
+ g->io_iteration = iteration;
+ }
+
+ return 0;
+}
+
+static int refresh_one(
+ const char *controller,
+ const char *path,
+ Hashmap *a,
+ Hashmap *b,
+ unsigned iteration,
+ unsigned depth) {
+
+ DIR *d = NULL;
+ int r;
+
+ assert(controller);
+ assert(path);
+ assert(a);
+
+ if (depth > arg_depth)
+ return 0;
+
+ r = process(controller, path, a, b, iteration);
+ if (r < 0)
+ return r;
+
+ r = cg_enumerate_subgroups(controller, path, &d);
+ if (r < 0) {
+ if (r == ENOENT)
+ return 0;
+
+ return r;
+ }
+
+ for (;;) {
+ char *fn, *p;
+
+ r = cg_read_subgroup(d, &fn);
+ if (r <= 0)
+ goto finish;
+
+ p = join(path, "/", fn, NULL);
+ free(fn);
+
+ if (!p) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ path_kill_slashes(p);
+
+ r = refresh_one(controller, p, a, b, iteration, depth + 1);
+ free(p);
+
+ if (r < 0)
+ goto finish;
+ }
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
+ int r;
+
+ assert(a);
+
+ r = refresh_one("name=systemd", "/", a, b, iteration, 0);
+ if (r < 0)
+ return r;
+
+ r = refresh_one("cpuacct", "/", a, b, iteration, 0);
+ if (r < 0)
+ return r;
+
+ r = refresh_one("memory", "/", a, b, iteration, 0);
+ if (r < 0)
+ return r;
+
+ return refresh_one("blkio", "/", a, b, iteration, 0);
+}
+
+static int group_compare(const void*a, const void *b) {
+ const Group *x = *(Group**)a, *y = *(Group**)b;
+
+ if (path_startswith(y->path, x->path))
+ return -1;
+ if (path_startswith(x->path, y->path))
+ return 1;
+
+ if (arg_order == ORDER_CPU) {
+ if (x->cpu_valid && y->cpu_valid) {
+
+ if (x->cpu_fraction > y->cpu_fraction)
+ return -1;
+ else if (x->cpu_fraction < y->cpu_fraction)
+ return 1;
+ } else if (x->cpu_valid)
+ return -1;
+ else if (y->cpu_valid)
+ return 1;
+ }
+
+ if (arg_order == ORDER_TASKS) {
+
+ if (x->n_tasks_valid && y->n_tasks_valid) {
+ if (x->n_tasks > y->n_tasks)
+ return -1;
+ else if (x->n_tasks < y->n_tasks)
+ return 1;
+ } else if (x->n_tasks_valid)
+ return -1;
+ else if (y->n_tasks_valid)
+ return 1;
+ }
+
+ if (arg_order == ORDER_MEMORY) {
+ if (x->memory_valid && y->memory_valid) {
+ if (x->memory > y->memory)
+ return -1;
+ else if (x->memory < y->memory)
+ return 1;
+ } else if (x->memory_valid)
+ return -1;
+ else if (y->memory_valid)
+ return 1;
+ }
+
+ if (arg_order == ORDER_IO) {
+ if (x->io_valid && y->io_valid) {
+ if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
+ return -1;
+ else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
+ return 1;
+ } else if (x->io_valid)
+ return -1;
+ else if (y->io_valid)
+ return 1;
+ }
+
+ return strcmp(x->path, y->path);
+}
+
+static int display(Hashmap *a) {
+ Iterator i;
+ Group *g;
+ Group **array;
+ unsigned rows, n = 0, j;
+
+ assert(a);
+
+ /* Set cursor to top left corner and clear screen */
+ fputs("\033[H"
+ "\033[2J", stdout);
+
+ array = alloca(sizeof(Group*) * hashmap_size(a));
+
+ HASHMAP_FOREACH(g, a, i)
+ if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
+ array[n++] = g;
+
+ qsort(array, n, sizeof(Group*), group_compare);
+
+ rows = fd_lines(STDOUT_FILENO);
+ if (rows <= 0)
+ rows = 25;
+
+ printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
+ arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", "Path", arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "",
+ arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks", arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "",
+ arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU", arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "",
+ arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory", arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
+ arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "",
+ arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "");
+
+ for (j = 0; j < n; j++) {
+ char *p;
+ char m[FORMAT_BYTES_MAX];
+
+ if (j + 5 > rows)
+ break;
+
+ g = array[j];
+
+ p = ellipsize(g->path, 37, 33);
+ printf("%-37s", p ? p : g->path);
+ free(p);
+
+ if (g->n_tasks_valid)
+ printf(" %7u", g->n_tasks);
+ else
+ fputs(" -", stdout);
+
+ if (g->cpu_valid)
+ printf(" %6.1f", g->cpu_fraction*100);
+ else
+ fputs(" -", stdout);
+
+ if (g->memory_valid)
+ printf(" %8s", format_bytes(m, sizeof(m), g->memory));
+ else
+ fputs(" -", stdout);
+
+ if (g->io_valid) {
+ printf(" %8s",
+ format_bytes(m, sizeof(m), g->io_input_bps));
+ printf(" %8s",
+ format_bytes(m, sizeof(m), g->io_output_bps));
+ } else
+ fputs(" - -", stdout);
+
+ putchar('\n');
+ }
+
+ return 0;
+}
+
+static void help(void) {
+
+ printf("%s [OPTIONS...]\n\n"
+ "Show top control groups by their resource usage.\n\n"
+ " -h --help Show this help\n"
+ " -p Order by path\n"
+ " -t Order by number of tasks\n"
+ " -c Order by CPU load\n"
+ " -m Order by memory load\n"
+ " -i Order by IO load\n"
+ " -d --delay=DELAY Specify delay\n"
+ " --depth=DEPTH Maximum traversal depth (default: 2)\n",
+ program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_DEPTH = 0x100
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "delay", required_argument, NULL, 'd' },
+ { "depth", required_argument, NULL, ARG_DEPTH },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+ int r;
+
+ assert(argc >= 1);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_DEPTH:
+ r = safe_atou(optarg, &arg_depth);
+ if (r < 0) {
+ log_error("Failed to parse depth parameter.");
+ return -EINVAL;
+ }
+
+ break;
+
+ case 'd':
+ r = parse_usec(optarg, &arg_delay);
+ if (r < 0 || arg_delay <= 0) {
+ log_error("Failed to parse delay parameter.");
+ return -EINVAL;
+ }
+
+ break;
+
+ case 'p':
+ arg_order = ORDER_PATH;
+ break;
+
+ case 't':
+ arg_order = ORDER_TASKS;
+ break;
+
+ case 'c':
+ arg_order = ORDER_CPU;
+ break;
+
+ case 'm':
+ arg_order = ORDER_MEMORY;
+ break;
+
+ case 'i':
+ arg_order = ORDER_IO;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ Hashmap *a = NULL, *b = NULL;
+ unsigned iteration = 0;
+ usec_t last_refresh = 0;
+ bool quit = false, immediate_refresh = false;
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ goto finish;
+
+ a = hashmap_new(string_hash_func, string_compare_func);
+ b = hashmap_new(string_hash_func, string_compare_func);
+ if (!a || !b) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ while (!quit) {
+ Hashmap *c;
+ usec_t t;
+ char key;
+ char h[FORMAT_TIMESPAN_MAX];
+
+ t = now(CLOCK_MONOTONIC);
+
+ if (t >= last_refresh + arg_delay || immediate_refresh) {
+
+ r = refresh(a, b, iteration++);
+ if (r < 0)
+ goto finish;
+
+ group_hashmap_clear(b);
+
+ c = a;
+ a = b;
+ b = c;
+
+ last_refresh = t;
+ immediate_refresh = false;
+ }
+
+ r = display(b);
+ if (r < 0)
+ goto finish;
+
+ r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
+ if (r == -ETIMEDOUT)
+ continue;
+ if (r < 0) {
+ log_error("Couldn't read key: %s", strerror(-r));
+ goto finish;
+ }
+
+ fputs("\r \r", stdout);
+ fflush(stdout);
+
+ switch (key) {
+
+ case ' ':
+ immediate_refresh = true;
+ break;
+
+ case 'q':
+ quit = true;
+ break;
+
+ case 'p':
+ arg_order = ORDER_PATH;
+ break;
+
+ case 't':
+ arg_order = ORDER_TASKS;
+ break;
+
+ case 'c':
+ arg_order = ORDER_CPU;
+ break;
+
+ case 'm':
+ arg_order = ORDER_MEMORY;
+ break;
+
+ case 'i':
+ arg_order = ORDER_IO;
+ break;
+
+ case '+':
+ if (arg_delay < USEC_PER_SEC)
+ arg_delay += USEC_PER_MSEC*250;
+ else
+ arg_delay += USEC_PER_SEC;
+
+ fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+ fflush(stdout);
+ sleep(1);
+ break;
+
+ case '-':
+ if (arg_delay <= USEC_PER_MSEC*500)
+ arg_delay = USEC_PER_MSEC*250;
+ else if (arg_delay < USEC_PER_MSEC*1250)
+ arg_delay -= USEC_PER_MSEC*250;
+ else
+ arg_delay -= USEC_PER_SEC;
+
+ fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+ fflush(stdout);
+ sleep(1);
+ break;
+
+ case '?':
+ case 'h':
+ fprintf(stdout,
+ "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
+ "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
+ fflush(stdout);
+ sleep(3);
+ break;
+
+ default:
+ fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
+ fflush(stdout);
+ sleep(1);
+ break;
+ }
+ }
+
+ log_info("Exiting.");
+
+ r = 0;
+
+finish:
+ group_hashmap_free(a);
+ group_hashmap_free(b);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/detect-virt.c b/src/detect-virt.c
deleted file mode 100644
index a83fb3c..0000000
--- a/src/detect-virt.c
+++ /dev/null
@@ -1,171 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <getopt.h>
-
-#include "util.h"
-#include "virt.h"
-#include "build.h"
-
-static bool arg_quiet = false;
-static enum {
- ANY_VIRTUALIZATION,
- ONLY_VM,
- ONLY_CONTAINER
-} arg_mode = ANY_VIRTUALIZATION;
-
-static int help(void) {
-
- printf("%s [OPTIONS...]\n\n"
- "Detect execution in a virtualized environment.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -c --container Only detect whether we are run in a container\n"
- " -v --vm Only detect whether we are run in a VM\n"
- " -q --quiet Don't output anything, just set return value\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_VERSION = 0x100
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "container", no_argument, NULL, 'c' },
- { "vm", optional_argument, NULL, 'v' },
- { "quiet", required_argument, NULL, 'q' },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(DISTRIBUTION);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case 'q':
- arg_quiet = true;
- break;
-
- case 'c':
- arg_mode = ONLY_CONTAINER;
- break;
-
- case 'v':
- arg_mode = ONLY_VM;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind < argc) {
- help();
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- const char *id = NULL;
- int retval, r;
-
- /* This is mostly intended to be used for scripts which want
- * to detect whether we are being run in a virtualized
- * environment or not */
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
- switch (arg_mode) {
-
- case ANY_VIRTUALIZATION: {
- Virtualization v;
-
- v = detect_virtualization(&id);
- if (v < 0) {
- log_error("Failed to check for virtualization: %s", strerror(-v));
- return EXIT_FAILURE;
- }
-
- retval = v != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
- break;
- }
-
- case ONLY_CONTAINER:
- r = detect_container(&id);
- if (r < 0) {
- log_error("Failed to check for container: %s", strerror(-r));
- return EXIT_FAILURE;
- }
-
- retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
- break;
-
- case ONLY_VM:
- r = detect_vm(&id);
- if (r < 0) {
- log_error("Failed to check for vm: %s", strerror(-r));
- return EXIT_FAILURE;
- }
-
- retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
- break;
- }
-
- if (!arg_quiet)
- puts(id ? id : "none");
-
- return retval;
-}
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
new file mode 100644
index 0000000..a83fb3c
--- /dev/null
+++ b/src/detect-virt/detect-virt.c
@@ -0,0 +1,171 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "virt.h"
+#include "build.h"
+
+static bool arg_quiet = false;
+static enum {
+ ANY_VIRTUALIZATION,
+ ONLY_VM,
+ ONLY_CONTAINER
+} arg_mode = ANY_VIRTUALIZATION;
+
+static int help(void) {
+
+ printf("%s [OPTIONS...]\n\n"
+ "Detect execution in a virtualized environment.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -c --container Only detect whether we are run in a container\n"
+ " -v --vm Only detect whether we are run in a VM\n"
+ " -q --quiet Don't output anything, just set return value\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "container", no_argument, NULL, 'c' },
+ { "vm", optional_argument, NULL, 'v' },
+ { "quiet", required_argument, NULL, 'q' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(DISTRIBUTION);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
+ case 'c':
+ arg_mode = ONLY_CONTAINER;
+ break;
+
+ case 'v':
+ arg_mode = ONLY_VM;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ help();
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ const char *id = NULL;
+ int retval, r;
+
+ /* This is mostly intended to be used for scripts which want
+ * to detect whether we are being run in a virtualized
+ * environment or not */
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ switch (arg_mode) {
+
+ case ANY_VIRTUALIZATION: {
+ Virtualization v;
+
+ v = detect_virtualization(&id);
+ if (v < 0) {
+ log_error("Failed to check for virtualization: %s", strerror(-v));
+ return EXIT_FAILURE;
+ }
+
+ retval = v != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
+ break;
+ }
+
+ case ONLY_CONTAINER:
+ r = detect_container(&id);
+ if (r < 0) {
+ log_error("Failed to check for container: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ break;
+
+ case ONLY_VM:
+ r = detect_vm(&id);
+ if (r < 0) {
+ log_error("Failed to check for vm: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ break;
+ }
+
+ if (!arg_quiet)
+ puts(id ? id : "none");
+
+ return retval;
+}
diff --git a/src/fsck.c b/src/fsck.c
deleted file mode 100644
index f25ec49..0000000
--- a/src/fsck.c
+++ /dev/null
@@ -1,406 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/file.h>
-
-#include <libudev.h>
-#include <dbus/dbus.h>
-
-#include "util.h"
-#include "dbus-common.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "virt.h"
-
-static bool arg_skip = false;
-static bool arg_force = false;
-static bool arg_show_progress = false;
-
-static void start_target(const char *target, bool isolate) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- const char *mode, *basic_target = "basic.target";
- DBusConnection *bus = NULL;
-
- assert(target);
-
- dbus_error_init(&error);
-
- if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
- log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
- goto finish;
- }
-
- if (isolate)
- mode = "isolate";
- else
- mode = "replace";
-
- log_info("Running request %s/start/%s", target, mode);
-
- if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
- log_error("Could not allocate message.");
- goto finish;
- }
-
- /* Start these units only if we can replace base.target with it */
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &basic_target,
- DBUS_TYPE_STRING, &target,
- DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID)) {
- log_error("Could not attach target and flag information to message.");
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
- /* Don't print a warning if we aren't called during
- * startup */
- if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
- log_error("Failed to start unit: %s", bus_error_message(&error));
-
- goto finish;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- if (bus) {
- dbus_connection_flush(bus);
- dbus_connection_close(bus);
- dbus_connection_unref(bus);
- }
-
- dbus_error_free(&error);
-}
-
-static int parse_proc_cmdline(void) {
- char *line, *w, *state;
- int r;
- size_t l;
-
- if (detect_container(NULL) > 0)
- return 0;
-
- if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
- log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
- return 0;
- }
-
- FOREACH_WORD_QUOTED(w, l, line, state) {
-
- if (strneq(w, "fsck.mode=auto", l))
- arg_force = arg_skip = false;
- else if (strneq(w, "fsck.mode=force", l))
- arg_force = true;
- else if (strneq(w, "fsck.mode=skip", l))
- arg_skip = true;
- else if (startswith(w, "fsck.mode"))
- log_warning("Invalid fsck.mode= parameter. Ignoring.");
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
- else if (strneq(w, "fastboot", l))
- arg_skip = true;
- else if (strneq(w, "forcefsck", l))
- arg_force = true;
-#endif
- }
-
- free(line);
- return 0;
-}
-
-static void test_files(void) {
- if (access("/fastboot", F_OK) >= 0)
- arg_skip = true;
-
- if (access("/forcefsck", F_OK) >= 0)
- arg_force = true;
-
- if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
- arg_show_progress = true;
-}
-
-static double percent(int pass, unsigned long cur, unsigned long max) {
- /* Values stolen from e2fsck */
-
- static const int pass_table[] = {
- 0, 70, 90, 92, 95, 100
- };
-
- if (pass <= 0)
- return 0.0;
-
- if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
- return 100.0;
-
- return (double) pass_table[pass-1] +
- ((double) pass_table[pass] - (double) pass_table[pass-1]) *
- (double) cur / (double) max;
-}
-
-static int process_progress(int fd) {
- FILE *f, *console;
- usec_t last = 0;
- bool locked = false;
- int clear = 0;
-
- f = fdopen(fd, "r");
- if (!f) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- console = fopen("/dev/console", "w");
- if (!console) {
- fclose(f);
- return -ENOMEM;
- }
-
- while (!feof(f)) {
- int pass, m;
- unsigned long cur, max;
- char *device;
- double p;
- usec_t t;
-
- if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
- break;
-
- /* Only show one progress counter at max */
- if (!locked) {
- if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
- free(device);
- continue;
- }
-
- locked = true;
- }
-
- /* Only update once every 50ms */
- t = now(CLOCK_MONOTONIC);
- if (last + 50 * USEC_PER_MSEC > t) {
- free(device);
- continue;
- }
-
- last = t;
-
- p = percent(pass, cur, max);
- fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
- fflush(console);
-
- free(device);
-
- if (m > clear)
- clear = m;
- }
-
- if (clear > 0) {
- unsigned j;
-
- fputc('\r', console);
- for (j = 0; j < (unsigned) clear; j++)
- fputc(' ', console);
- fputc('\r', console);
- fflush(console);
- }
-
- fclose(f);
- fclose(console);
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- const char *cmdline[9];
- int i = 0, r = EXIT_FAILURE, q;
- pid_t pid;
- siginfo_t status;
- struct udev *udev = NULL;
- struct udev_device *udev_device = NULL;
- const char *device;
- bool root_directory;
- int progress_pipe[2] = { -1, -1 };
- char dash_c[2+10+1];
-
- if (argc > 2) {
- log_error("This program expects one or no arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- parse_proc_cmdline();
- test_files();
-
- if (!arg_force && arg_skip)
- return 0;
-
- if (argc > 1) {
- device = argv[1];
- root_directory = false;
- } else {
- struct stat st;
- struct timespec times[2];
-
- /* Find root device */
-
- if (stat("/", &st) < 0) {
- log_error("Failed to stat() the root directory: %m");
- goto finish;
- }
-
- /* Virtual root devices don't need an fsck */
- if (major(st.st_dev) == 0)
- return 0;
-
- /* check if we are already writable */
- times[0] = st.st_atim;
- times[1] = st.st_mtim;
- if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
- log_info("Root directory is writable, skipping check.");
- return 0;
- }
-
- if (!(udev = udev_new())) {
- log_error("Out of memory");
- goto finish;
- }
-
- if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
- log_error("Failed to detect root device.");
- goto finish;
- }
-
- if (!(device = udev_device_get_devnode(udev_device))) {
- log_error("Failed to detect device node of root directory.");
- goto finish;
- }
-
- root_directory = true;
- }
-
- if (arg_show_progress)
- if (pipe(progress_pipe) < 0) {
- log_error("pipe(): %m");
- goto finish;
- }
-
- cmdline[i++] = "/sbin/fsck";
- cmdline[i++] = "-a";
- cmdline[i++] = "-T";
- cmdline[i++] = "-l";
-
- if (!root_directory)
- cmdline[i++] = "-M";
-
- if (arg_force)
- cmdline[i++] = "-f";
-
- if (progress_pipe[1] >= 0) {
- snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
- char_array_0(dash_c);
- cmdline[i++] = dash_c;
- }
-
- cmdline[i++] = device;
- cmdline[i++] = NULL;
-
- pid = fork();
- if (pid < 0) {
- log_error("fork(): %m");
- goto finish;
- } else if (pid == 0) {
- /* Child */
- if (progress_pipe[0] >= 0)
- close_nointr_nofail(progress_pipe[0]);
- execv(cmdline[0], (char**) cmdline);
- _exit(8); /* Operational error */
- }
-
- if (progress_pipe[1] >= 0) {
- close_nointr_nofail(progress_pipe[1]);
- progress_pipe[1] = -1;
- }
-
- if (progress_pipe[0] >= 0) {
- process_progress(progress_pipe[0]);
- progress_pipe[0] = -1;
- }
-
- q = wait_for_terminate(pid, &status);
- if (q < 0) {
- log_error("waitid(): %s", strerror(-q));
- goto finish;
- }
-
- if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
-
- if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
- log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
- else if (status.si_code == CLD_EXITED)
- log_error("fsck failed with error code %i.", status.si_status);
- else
- log_error("fsck failed due to unknown reason.");
-
- if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
- /* System should be rebooted. */
- start_target(SPECIAL_REBOOT_TARGET, false);
- else if (status.si_code == CLD_EXITED && (status.si_status & 6))
- /* Some other problem */
- start_target(SPECIAL_EMERGENCY_TARGET, true);
- else {
- r = EXIT_SUCCESS;
- log_warning("Ignoring error.");
- }
-
- } else
- r = EXIT_SUCCESS;
-
- if (status.si_code == CLD_EXITED && (status.si_status & 1))
- touch("/run/systemd/quotacheck");
-
-finish:
- if (udev_device)
- udev_device_unref(udev_device);
-
- if (udev)
- udev_unref(udev);
-
- close_pipe(progress_pipe);
-
- return r;
-}
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
new file mode 100644
index 0000000..f25ec49
--- /dev/null
+++ b/src/fsck/fsck.c
@@ -0,0 +1,406 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#include <libudev.h>
+#include <dbus/dbus.h>
+
+#include "util.h"
+#include "dbus-common.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "virt.h"
+
+static bool arg_skip = false;
+static bool arg_force = false;
+static bool arg_show_progress = false;
+
+static void start_target(const char *target, bool isolate) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ const char *mode, *basic_target = "basic.target";
+ DBusConnection *bus = NULL;
+
+ assert(target);
+
+ dbus_error_init(&error);
+
+ if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
+ log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+ goto finish;
+ }
+
+ if (isolate)
+ mode = "isolate";
+ else
+ mode = "replace";
+
+ log_info("Running request %s/start/%s", target, mode);
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
+ log_error("Could not allocate message.");
+ goto finish;
+ }
+
+ /* Start these units only if we can replace base.target with it */
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &basic_target,
+ DBUS_TYPE_STRING, &target,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not attach target and flag information to message.");
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+ /* Don't print a warning if we aren't called during
+ * startup */
+ if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
+ log_error("Failed to start unit: %s", bus_error_message(&error));
+
+ goto finish;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (bus) {
+ dbus_connection_flush(bus);
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ dbus_error_free(&error);
+}
+
+static int parse_proc_cmdline(void) {
+ char *line, *w, *state;
+ int r;
+ size_t l;
+
+ if (detect_container(NULL) > 0)
+ return 0;
+
+ if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+ return 0;
+ }
+
+ FOREACH_WORD_QUOTED(w, l, line, state) {
+
+ if (strneq(w, "fsck.mode=auto", l))
+ arg_force = arg_skip = false;
+ else if (strneq(w, "fsck.mode=force", l))
+ arg_force = true;
+ else if (strneq(w, "fsck.mode=skip", l))
+ arg_skip = true;
+ else if (startswith(w, "fsck.mode"))
+ log_warning("Invalid fsck.mode= parameter. Ignoring.");
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+ else if (strneq(w, "fastboot", l))
+ arg_skip = true;
+ else if (strneq(w, "forcefsck", l))
+ arg_force = true;
+#endif
+ }
+
+ free(line);
+ return 0;
+}
+
+static void test_files(void) {
+ if (access("/fastboot", F_OK) >= 0)
+ arg_skip = true;
+
+ if (access("/forcefsck", F_OK) >= 0)
+ arg_force = true;
+
+ if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
+ arg_show_progress = true;
+}
+
+static double percent(int pass, unsigned long cur, unsigned long max) {
+ /* Values stolen from e2fsck */
+
+ static const int pass_table[] = {
+ 0, 70, 90, 92, 95, 100
+ };
+
+ if (pass <= 0)
+ return 0.0;
+
+ if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
+ return 100.0;
+
+ return (double) pass_table[pass-1] +
+ ((double) pass_table[pass] - (double) pass_table[pass-1]) *
+ (double) cur / (double) max;
+}
+
+static int process_progress(int fd) {
+ FILE *f, *console;
+ usec_t last = 0;
+ bool locked = false;
+ int clear = 0;
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ console = fopen("/dev/console", "w");
+ if (!console) {
+ fclose(f);
+ return -ENOMEM;
+ }
+
+ while (!feof(f)) {
+ int pass, m;
+ unsigned long cur, max;
+ char *device;
+ double p;
+ usec_t t;
+
+ if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
+ break;
+
+ /* Only show one progress counter at max */
+ if (!locked) {
+ if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
+ free(device);
+ continue;
+ }
+
+ locked = true;
+ }
+
+ /* Only update once every 50ms */
+ t = now(CLOCK_MONOTONIC);
+ if (last + 50 * USEC_PER_MSEC > t) {
+ free(device);
+ continue;
+ }
+
+ last = t;
+
+ p = percent(pass, cur, max);
+ fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
+ fflush(console);
+
+ free(device);
+
+ if (m > clear)
+ clear = m;
+ }
+
+ if (clear > 0) {
+ unsigned j;
+
+ fputc('\r', console);
+ for (j = 0; j < (unsigned) clear; j++)
+ fputc(' ', console);
+ fputc('\r', console);
+ fflush(console);
+ }
+
+ fclose(f);
+ fclose(console);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ const char *cmdline[9];
+ int i = 0, r = EXIT_FAILURE, q;
+ pid_t pid;
+ siginfo_t status;
+ struct udev *udev = NULL;
+ struct udev_device *udev_device = NULL;
+ const char *device;
+ bool root_directory;
+ int progress_pipe[2] = { -1, -1 };
+ char dash_c[2+10+1];
+
+ if (argc > 2) {
+ log_error("This program expects one or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ parse_proc_cmdline();
+ test_files();
+
+ if (!arg_force && arg_skip)
+ return 0;
+
+ if (argc > 1) {
+ device = argv[1];
+ root_directory = false;
+ } else {
+ struct stat st;
+ struct timespec times[2];
+
+ /* Find root device */
+
+ if (stat("/", &st) < 0) {
+ log_error("Failed to stat() the root directory: %m");
+ goto finish;
+ }
+
+ /* Virtual root devices don't need an fsck */
+ if (major(st.st_dev) == 0)
+ return 0;
+
+ /* check if we are already writable */
+ times[0] = st.st_atim;
+ times[1] = st.st_mtim;
+ if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
+ log_info("Root directory is writable, skipping check.");
+ return 0;
+ }
+
+ if (!(udev = udev_new())) {
+ log_error("Out of memory");
+ goto finish;
+ }
+
+ if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
+ log_error("Failed to detect root device.");
+ goto finish;
+ }
+
+ if (!(device = udev_device_get_devnode(udev_device))) {
+ log_error("Failed to detect device node of root directory.");
+ goto finish;
+ }
+
+ root_directory = true;
+ }
+
+ if (arg_show_progress)
+ if (pipe(progress_pipe) < 0) {
+ log_error("pipe(): %m");
+ goto finish;
+ }
+
+ cmdline[i++] = "/sbin/fsck";
+ cmdline[i++] = "-a";
+ cmdline[i++] = "-T";
+ cmdline[i++] = "-l";
+
+ if (!root_directory)
+ cmdline[i++] = "-M";
+
+ if (arg_force)
+ cmdline[i++] = "-f";
+
+ if (progress_pipe[1] >= 0) {
+ snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
+ char_array_0(dash_c);
+ cmdline[i++] = dash_c;
+ }
+
+ cmdline[i++] = device;
+ cmdline[i++] = NULL;
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("fork(): %m");
+ goto finish;
+ } else if (pid == 0) {
+ /* Child */
+ if (progress_pipe[0] >= 0)
+ close_nointr_nofail(progress_pipe[0]);
+ execv(cmdline[0], (char**) cmdline);
+ _exit(8); /* Operational error */
+ }
+
+ if (progress_pipe[1] >= 0) {
+ close_nointr_nofail(progress_pipe[1]);
+ progress_pipe[1] = -1;
+ }
+
+ if (progress_pipe[0] >= 0) {
+ process_progress(progress_pipe[0]);
+ progress_pipe[0] = -1;
+ }
+
+ q = wait_for_terminate(pid, &status);
+ if (q < 0) {
+ log_error("waitid(): %s", strerror(-q));
+ goto finish;
+ }
+
+ if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
+
+ if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
+ log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
+ else if (status.si_code == CLD_EXITED)
+ log_error("fsck failed with error code %i.", status.si_status);
+ else
+ log_error("fsck failed due to unknown reason.");
+
+ if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
+ /* System should be rebooted. */
+ start_target(SPECIAL_REBOOT_TARGET, false);
+ else if (status.si_code == CLD_EXITED && (status.si_status & 6))
+ /* Some other problem */
+ start_target(SPECIAL_EMERGENCY_TARGET, true);
+ else {
+ r = EXIT_SUCCESS;
+ log_warning("Ignoring error.");
+ }
+
+ } else
+ r = EXIT_SUCCESS;
+
+ if (status.si_code == CLD_EXITED && (status.si_status & 1))
+ touch("/run/systemd/quotacheck");
+
+finish:
+ if (udev_device)
+ udev_device_unref(udev_device);
+
+ if (udev)
+ udev_unref(udev);
+
+ close_pipe(progress_pipe);
+
+ return r;
+}
diff --git a/src/getty-generator.c b/src/getty-generator.c
deleted file mode 100644
index 58ad8a6..0000000
--- a/src/getty-generator.c
+++ /dev/null
@@ -1,183 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "unit-name.h"
-#include "virt.h"
-
-static const char *arg_dest = "/tmp";
-
-static int add_symlink(const char *fservice, const char *tservice) {
- char *from = NULL, *to = NULL;
- int r;
-
- assert(fservice);
- assert(tservice);
-
- asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice);
- asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice);
-
- if (!from || !to) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- mkdir_parents(to, 0755);
-
- r = symlink(from, to);
- if (r < 0) {
- if (errno == EEXIST)
- /* In case console=hvc0 is passed this will very likely result in EEXIST */
- r = 0;
- else {
- log_error("Failed to create symlink from %s to %s: %m", from, to);
- r = -errno;
- }
- }
-
-finish:
-
- free(from);
- free(to);
-
- return r;
-}
-
-static int add_serial_getty(const char *tty) {
- char *n;
- int r;
-
- assert(tty);
-
- log_debug("Automatically adding serial getty for /dev/%s.", tty);
-
- n = unit_name_replace_instance("serial-getty at .service", tty);
- if (!n) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- r = add_symlink("serial-getty at .service", n);
- free(n);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
-
- static const char virtualization_consoles[] =
- "hvc0\0"
- "xvc0\0"
- "hvsi0\0";
-
- int r = EXIT_SUCCESS;
- char *active;
- const char *j;
-
- if (argc > 2) {
- log_error("This program takes one or no arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (argc > 1)
- arg_dest = argv[1];
-
- if (detect_container(NULL) > 0) {
- log_debug("Automatically adding console shell.");
-
- if (add_symlink("console-shell.service", "console-shell.service") < 0)
- r = EXIT_FAILURE;
-
- /* Don't add any further magic if we are in a container */
- goto finish;
- }
-
- if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
- const char *tty;
-
- tty = strrchr(active, ' ');
- if (tty)
- tty ++;
- else
- tty = active;
-
- /* Automatically add in a serial getty on the kernel
- * console */
- if (tty_is_vc(tty))
- free(active);
- else {
- int k;
-
- /* We assume that gettys on virtual terminals are
- * started via manual configuration and do this magic
- * only for non-VC terminals. */
-
- k = add_serial_getty(tty);
- free(active);
-
- if (k < 0) {
- r = EXIT_FAILURE;
- goto finish;
- }
- }
- }
-
- /* Automatically add in a serial getty on the first
- * virtualizer console */
- NULSTR_FOREACH(j, virtualization_consoles) {
- char *p;
- int k;
-
- if (asprintf(&p, "/sys/class/tty/%s", j) < 0) {
- log_error("Out of memory");
- r = EXIT_FAILURE;
- goto finish;
- }
-
- k = access(p, F_OK);
- free(p);
-
- if (k < 0)
- continue;
-
- k = add_serial_getty(j);
- if (k < 0) {
- r = EXIT_FAILURE;
- goto finish;
- }
- }
-
-finish:
- return r;
-}
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
new file mode 100644
index 0000000..58ad8a6
--- /dev/null
+++ b/src/getty-generator/getty-generator.c
@@ -0,0 +1,183 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "unit-name.h"
+#include "virt.h"
+
+static const char *arg_dest = "/tmp";
+
+static int add_symlink(const char *fservice, const char *tservice) {
+ char *from = NULL, *to = NULL;
+ int r;
+
+ assert(fservice);
+ assert(tservice);
+
+ asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice);
+ asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice);
+
+ if (!from || !to) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ mkdir_parents(to, 0755);
+
+ r = symlink(from, to);
+ if (r < 0) {
+ if (errno == EEXIST)
+ /* In case console=hvc0 is passed this will very likely result in EEXIST */
+ r = 0;
+ else {
+ log_error("Failed to create symlink from %s to %s: %m", from, to);
+ r = -errno;
+ }
+ }
+
+finish:
+
+ free(from);
+ free(to);
+
+ return r;
+}
+
+static int add_serial_getty(const char *tty) {
+ char *n;
+ int r;
+
+ assert(tty);
+
+ log_debug("Automatically adding serial getty for /dev/%s.", tty);
+
+ n = unit_name_replace_instance("serial-getty at .service", tty);
+ if (!n) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ r = add_symlink("serial-getty at .service", n);
+ free(n);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+
+ static const char virtualization_consoles[] =
+ "hvc0\0"
+ "xvc0\0"
+ "hvsi0\0";
+
+ int r = EXIT_SUCCESS;
+ char *active;
+ const char *j;
+
+ if (argc > 2) {
+ log_error("This program takes one or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if (argc > 1)
+ arg_dest = argv[1];
+
+ if (detect_container(NULL) > 0) {
+ log_debug("Automatically adding console shell.");
+
+ if (add_symlink("console-shell.service", "console-shell.service") < 0)
+ r = EXIT_FAILURE;
+
+ /* Don't add any further magic if we are in a container */
+ goto finish;
+ }
+
+ if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+ const char *tty;
+
+ tty = strrchr(active, ' ');
+ if (tty)
+ tty ++;
+ else
+ tty = active;
+
+ /* Automatically add in a serial getty on the kernel
+ * console */
+ if (tty_is_vc(tty))
+ free(active);
+ else {
+ int k;
+
+ /* We assume that gettys on virtual terminals are
+ * started via manual configuration and do this magic
+ * only for non-VC terminals. */
+
+ k = add_serial_getty(tty);
+ free(active);
+
+ if (k < 0) {
+ r = EXIT_FAILURE;
+ goto finish;
+ }
+ }
+ }
+
+ /* Automatically add in a serial getty on the first
+ * virtualizer console */
+ NULSTR_FOREACH(j, virtualization_consoles) {
+ char *p;
+ int k;
+
+ if (asprintf(&p, "/sys/class/tty/%s", j) < 0) {
+ log_error("Out of memory");
+ r = EXIT_FAILURE;
+ goto finish;
+ }
+
+ k = access(p, F_OK);
+ free(p);
+
+ if (k < 0)
+ continue;
+
+ k = add_serial_getty(j);
+ if (k < 0) {
+ r = EXIT_FAILURE;
+ goto finish;
+ }
+ }
+
+finish:
+ return r;
+}
diff --git a/src/initctl.c b/src/initctl.c
deleted file mode 100644
index 0eb008d..0000000
--- a/src/initctl.c
+++ /dev/null
@@ -1,451 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <time.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <sys/epoll.h>
-#include <sys/un.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include <dbus/dbus.h>
-#include <systemd/sd-daemon.h>
-
-#include "util.h"
-#include "log.h"
-#include "list.h"
-#include "initreq.h"
-#include "special.h"
-#include "dbus-common.h"
-#include "def.h"
-
-#define SERVER_FD_MAX 16
-#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
-
-typedef struct Fifo Fifo;
-
-typedef struct Server {
- int epoll_fd;
-
- LIST_HEAD(Fifo, fifos);
- unsigned n_fifos;
-
- DBusConnection *bus;
-
- bool quit;
-} Server;
-
-struct Fifo {
- Server *server;
-
- int fd;
-
- struct init_request buffer;
- size_t bytes_read;
-
- LIST_FIELDS(Fifo, fifo);
-};
-
-static const char *translate_runlevel(int runlevel, bool *isolate) {
- static const struct {
- const int runlevel;
- const char *special;
- bool isolate;
- } table[] = {
- { '0', SPECIAL_POWEROFF_TARGET, false },
- { '1', SPECIAL_RESCUE_TARGET, true },
- { 's', SPECIAL_RESCUE_TARGET, true },
- { 'S', SPECIAL_RESCUE_TARGET, true },
- { '2', SPECIAL_RUNLEVEL2_TARGET, true },
- { '3', SPECIAL_RUNLEVEL3_TARGET, true },
- { '4', SPECIAL_RUNLEVEL4_TARGET, true },
- { '5', SPECIAL_RUNLEVEL5_TARGET, true },
- { '6', SPECIAL_REBOOT_TARGET, false },
- };
-
- unsigned i;
-
- assert(isolate);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (table[i].runlevel == runlevel) {
- *isolate = table[i].isolate;
- if (runlevel == '6' && kexec_loaded())
- return SPECIAL_KEXEC_TARGET;
- return table[i].special;
- }
-
- return NULL;
-}
-
-static void change_runlevel(Server *s, int runlevel) {
- const char *target;
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- const char *mode;
- bool isolate = false;
-
- assert(s);
-
- dbus_error_init(&error);
-
- if (!(target = translate_runlevel(runlevel, &isolate))) {
- log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
- goto finish;
- }
-
- if (isolate)
- mode = "isolate";
- else
- mode = "replace";
-
- log_debug("Running request %s/start/%s", target, mode);
-
- if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
- log_error("Could not allocate message.");
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &target,
- DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID)) {
- log_error("Could not attach target and flag information to message.");
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
- log_error("Failed to start unit: %s", bus_error_message(&error));
- goto finish;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-}
-
-static void request_process(Server *s, const struct init_request *req) {
- assert(s);
- assert(req);
-
- if (req->magic != INIT_MAGIC) {
- log_error("Got initctl request with invalid magic. Ignoring.");
- return;
- }
-
- switch (req->cmd) {
-
- case INIT_CMD_RUNLVL:
- if (!isprint(req->runlevel))
- log_error("Got invalid runlevel. Ignoring.");
- else
- switch (req->runlevel) {
-
- /* we are async anyway, so just use kill for reexec/reload */
- case 'u':
- case 'U':
- if (kill(1, SIGTERM) < 0)
- log_error("kill() failed: %m");
-
- /* The bus connection will be
- * terminated if PID 1 is reexecuted,
- * hence let's just exit here, and
- * rely on that we'll be restarted on
- * the next request */
- s->quit = true;
- break;
-
- case 'q':
- case 'Q':
- if (kill(1, SIGHUP) < 0)
- log_error("kill() failed: %m");
- break;
-
- default:
- change_runlevel(s, req->runlevel);
- }
- return;
-
- case INIT_CMD_POWERFAIL:
- case INIT_CMD_POWERFAILNOW:
- case INIT_CMD_POWEROK:
- log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
- return;
-
- case INIT_CMD_CHANGECONS:
- log_warning("Received console change initctl request. This is not implemented in systemd.");
- return;
-
- case INIT_CMD_SETENV:
- case INIT_CMD_UNSETENV:
- log_warning("Received environment initctl request. This is not implemented in systemd.");
- return;
-
- default:
- log_warning("Received unknown initctl request. Ignoring.");
- return;
- }
-}
-
-static int fifo_process(Fifo *f) {
- ssize_t l;
-
- assert(f);
-
- errno = EIO;
- if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
-
- if (errno == EAGAIN)
- return 0;
-
- log_warning("Failed to read from fifo: %s", strerror(errno));
- return -1;
- }
-
- f->bytes_read += l;
- assert(f->bytes_read <= sizeof(f->buffer));
-
- if (f->bytes_read == sizeof(f->buffer)) {
- request_process(f->server, &f->buffer);
- f->bytes_read = 0;
- }
-
- return 0;
-}
-
-static void fifo_free(Fifo *f) {
- assert(f);
-
- if (f->server) {
- assert(f->server->n_fifos > 0);
- f->server->n_fifos--;
- LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
- }
-
- if (f->fd >= 0) {
- if (f->server)
- epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
-
- close_nointr_nofail(f->fd);
- }
-
- free(f);
-}
-
-static void server_done(Server *s) {
- assert(s);
-
- while (s->fifos)
- fifo_free(s->fifos);
-
- if (s->epoll_fd >= 0)
- close_nointr_nofail(s->epoll_fd);
-
- if (s->bus) {
- dbus_connection_flush(s->bus);
- dbus_connection_close(s->bus);
- dbus_connection_unref(s->bus);
- }
-}
-
-static int server_init(Server *s, unsigned n_sockets) {
- int r;
- unsigned i;
- DBusError error;
-
- assert(s);
- assert(n_sockets > 0);
-
- dbus_error_init(&error);
-
- zero(*s);
-
- if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
- r = -errno;
- log_error("Failed to create epoll object: %s", strerror(errno));
- goto fail;
- }
-
- for (i = 0; i < n_sockets; i++) {
- struct epoll_event ev;
- Fifo *f;
- int fd;
-
- fd = SD_LISTEN_FDS_START+i;
-
- if ((r = sd_is_fifo(fd, NULL)) < 0) {
- log_error("Failed to determine file descriptor type: %s", strerror(-r));
- goto fail;
- }
-
- if (!r) {
- log_error("Wrong file descriptor type.");
- r = -EINVAL;
- goto fail;
- }
-
- if (!(f = new0(Fifo, 1))) {
- r = -ENOMEM;
- log_error("Failed to create fifo object: %s", strerror(errno));
- goto fail;
- }
-
- f->fd = -1;
-
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = f;
- if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
- r = -errno;
- fifo_free(f);
- log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
- goto fail;
- }
-
- f->fd = fd;
- LIST_PREPEND(Fifo, fifo, s->fifos, f);
- f->server = s;
- s->n_fifos ++;
- }
-
- if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
- log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
- goto fail;
- }
-
- return 0;
-
-fail:
- server_done(s);
-
- dbus_error_free(&error);
- return r;
-}
-
-static int process_event(Server *s, struct epoll_event *ev) {
- int r;
- Fifo *f;
-
- assert(s);
-
- if (!(ev->events & EPOLLIN)) {
- log_info("Got invalid event from epoll. (3)");
- return -EIO;
- }
-
- f = (Fifo*) ev->data.ptr;
-
- if ((r = fifo_process(f)) < 0) {
- log_info("Got error on fifo: %s", strerror(-r));
- fifo_free(f);
- return r;
- }
-
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- Server server;
- int r = EXIT_FAILURE, n;
-
- if (getppid() != 1) {
- log_error("This program should be invoked by init only.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1) {
- log_error("This program does not take arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if ((n = sd_listen_fds(true)) < 0) {
- log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
- return EXIT_FAILURE;
- }
-
- if (n <= 0 || n > SERVER_FD_MAX) {
- log_error("No or too many file descriptors passed.");
- return EXIT_FAILURE;
- }
-
- if (server_init(&server, (unsigned) n) < 0)
- return EXIT_FAILURE;
-
- log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- while (!server.quit) {
- struct epoll_event event;
- int k;
-
- if ((k = epoll_wait(server.epoll_fd,
- &event, 1,
- TIMEOUT_MSEC)) < 0) {
-
- if (errno == EINTR)
- continue;
-
- log_error("epoll_wait() failed: %s", strerror(errno));
- goto fail;
- }
-
- if (k <= 0)
- break;
-
- if (process_event(&server, &event) < 0)
- goto fail;
- }
-
- r = EXIT_SUCCESS;
-
- log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
-
-fail:
- sd_notify(false,
- "STATUS=Shutting down...");
-
- server_done(&server);
-
- dbus_shutdown();
-
- return r;
-}
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
new file mode 100644
index 0000000..0eb008d
--- /dev/null
+++ b/src/initctl/initctl.c
@@ -0,0 +1,451 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <dbus/dbus.h>
+#include <systemd/sd-daemon.h>
+
+#include "util.h"
+#include "log.h"
+#include "list.h"
+#include "initreq.h"
+#include "special.h"
+#include "dbus-common.h"
+#include "def.h"
+
+#define SERVER_FD_MAX 16
+#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
+
+typedef struct Fifo Fifo;
+
+typedef struct Server {
+ int epoll_fd;
+
+ LIST_HEAD(Fifo, fifos);
+ unsigned n_fifos;
+
+ DBusConnection *bus;
+
+ bool quit;
+} Server;
+
+struct Fifo {
+ Server *server;
+
+ int fd;
+
+ struct init_request buffer;
+ size_t bytes_read;
+
+ LIST_FIELDS(Fifo, fifo);
+};
+
+static const char *translate_runlevel(int runlevel, bool *isolate) {
+ static const struct {
+ const int runlevel;
+ const char *special;
+ bool isolate;
+ } table[] = {
+ { '0', SPECIAL_POWEROFF_TARGET, false },
+ { '1', SPECIAL_RESCUE_TARGET, true },
+ { 's', SPECIAL_RESCUE_TARGET, true },
+ { 'S', SPECIAL_RESCUE_TARGET, true },
+ { '2', SPECIAL_RUNLEVEL2_TARGET, true },
+ { '3', SPECIAL_RUNLEVEL3_TARGET, true },
+ { '4', SPECIAL_RUNLEVEL4_TARGET, true },
+ { '5', SPECIAL_RUNLEVEL5_TARGET, true },
+ { '6', SPECIAL_REBOOT_TARGET, false },
+ };
+
+ unsigned i;
+
+ assert(isolate);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (table[i].runlevel == runlevel) {
+ *isolate = table[i].isolate;
+ if (runlevel == '6' && kexec_loaded())
+ return SPECIAL_KEXEC_TARGET;
+ return table[i].special;
+ }
+
+ return NULL;
+}
+
+static void change_runlevel(Server *s, int runlevel) {
+ const char *target;
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ const char *mode;
+ bool isolate = false;
+
+ assert(s);
+
+ dbus_error_init(&error);
+
+ if (!(target = translate_runlevel(runlevel, &isolate))) {
+ log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
+ goto finish;
+ }
+
+ if (isolate)
+ mode = "isolate";
+ else
+ mode = "replace";
+
+ log_debug("Running request %s/start/%s", target, mode);
+
+ if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
+ log_error("Could not allocate message.");
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &target,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not attach target and flag information to message.");
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
+ log_error("Failed to start unit: %s", bus_error_message(&error));
+ goto finish;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+}
+
+static void request_process(Server *s, const struct init_request *req) {
+ assert(s);
+ assert(req);
+
+ if (req->magic != INIT_MAGIC) {
+ log_error("Got initctl request with invalid magic. Ignoring.");
+ return;
+ }
+
+ switch (req->cmd) {
+
+ case INIT_CMD_RUNLVL:
+ if (!isprint(req->runlevel))
+ log_error("Got invalid runlevel. Ignoring.");
+ else
+ switch (req->runlevel) {
+
+ /* we are async anyway, so just use kill for reexec/reload */
+ case 'u':
+ case 'U':
+ if (kill(1, SIGTERM) < 0)
+ log_error("kill() failed: %m");
+
+ /* The bus connection will be
+ * terminated if PID 1 is reexecuted,
+ * hence let's just exit here, and
+ * rely on that we'll be restarted on
+ * the next request */
+ s->quit = true;
+ break;
+
+ case 'q':
+ case 'Q':
+ if (kill(1, SIGHUP) < 0)
+ log_error("kill() failed: %m");
+ break;
+
+ default:
+ change_runlevel(s, req->runlevel);
+ }
+ return;
+
+ case INIT_CMD_POWERFAIL:
+ case INIT_CMD_POWERFAILNOW:
+ case INIT_CMD_POWEROK:
+ log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
+ return;
+
+ case INIT_CMD_CHANGECONS:
+ log_warning("Received console change initctl request. This is not implemented in systemd.");
+ return;
+
+ case INIT_CMD_SETENV:
+ case INIT_CMD_UNSETENV:
+ log_warning("Received environment initctl request. This is not implemented in systemd.");
+ return;
+
+ default:
+ log_warning("Received unknown initctl request. Ignoring.");
+ return;
+ }
+}
+
+static int fifo_process(Fifo *f) {
+ ssize_t l;
+
+ assert(f);
+
+ errno = EIO;
+ if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
+
+ if (errno == EAGAIN)
+ return 0;
+
+ log_warning("Failed to read from fifo: %s", strerror(errno));
+ return -1;
+ }
+
+ f->bytes_read += l;
+ assert(f->bytes_read <= sizeof(f->buffer));
+
+ if (f->bytes_read == sizeof(f->buffer)) {
+ request_process(f->server, &f->buffer);
+ f->bytes_read = 0;
+ }
+
+ return 0;
+}
+
+static void fifo_free(Fifo *f) {
+ assert(f);
+
+ if (f->server) {
+ assert(f->server->n_fifos > 0);
+ f->server->n_fifos--;
+ LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
+ }
+
+ if (f->fd >= 0) {
+ if (f->server)
+ epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
+
+ close_nointr_nofail(f->fd);
+ }
+
+ free(f);
+}
+
+static void server_done(Server *s) {
+ assert(s);
+
+ while (s->fifos)
+ fifo_free(s->fifos);
+
+ if (s->epoll_fd >= 0)
+ close_nointr_nofail(s->epoll_fd);
+
+ if (s->bus) {
+ dbus_connection_flush(s->bus);
+ dbus_connection_close(s->bus);
+ dbus_connection_unref(s->bus);
+ }
+}
+
+static int server_init(Server *s, unsigned n_sockets) {
+ int r;
+ unsigned i;
+ DBusError error;
+
+ assert(s);
+ assert(n_sockets > 0);
+
+ dbus_error_init(&error);
+
+ zero(*s);
+
+ if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ r = -errno;
+ log_error("Failed to create epoll object: %s", strerror(errno));
+ goto fail;
+ }
+
+ for (i = 0; i < n_sockets; i++) {
+ struct epoll_event ev;
+ Fifo *f;
+ int fd;
+
+ fd = SD_LISTEN_FDS_START+i;
+
+ if ((r = sd_is_fifo(fd, NULL)) < 0) {
+ log_error("Failed to determine file descriptor type: %s", strerror(-r));
+ goto fail;
+ }
+
+ if (!r) {
+ log_error("Wrong file descriptor type.");
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (!(f = new0(Fifo, 1))) {
+ r = -ENOMEM;
+ log_error("Failed to create fifo object: %s", strerror(errno));
+ goto fail;
+ }
+
+ f->fd = -1;
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = f;
+ if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ r = -errno;
+ fifo_free(f);
+ log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
+ goto fail;
+ }
+
+ f->fd = fd;
+ LIST_PREPEND(Fifo, fifo, s->fifos, f);
+ f->server = s;
+ s->n_fifos ++;
+ }
+
+ if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
+ log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ server_done(s);
+
+ dbus_error_free(&error);
+ return r;
+}
+
+static int process_event(Server *s, struct epoll_event *ev) {
+ int r;
+ Fifo *f;
+
+ assert(s);
+
+ if (!(ev->events & EPOLLIN)) {
+ log_info("Got invalid event from epoll. (3)");
+ return -EIO;
+ }
+
+ f = (Fifo*) ev->data.ptr;
+
+ if ((r = fifo_process(f)) < 0) {
+ log_info("Got error on fifo: %s", strerror(-r));
+ fifo_free(f);
+ return r;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ Server server;
+ int r = EXIT_FAILURE, n;
+
+ if (getppid() != 1) {
+ log_error("This program should be invoked by init only.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1) {
+ log_error("This program does not take arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if ((n = sd_listen_fds(true)) < 0) {
+ log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ if (n <= 0 || n > SERVER_FD_MAX) {
+ log_error("No or too many file descriptors passed.");
+ return EXIT_FAILURE;
+ }
+
+ if (server_init(&server, (unsigned) n) < 0)
+ return EXIT_FAILURE;
+
+ log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing requests...");
+
+ while (!server.quit) {
+ struct epoll_event event;
+ int k;
+
+ if ((k = epoll_wait(server.epoll_fd,
+ &event, 1,
+ TIMEOUT_MSEC)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("epoll_wait() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (k <= 0)
+ break;
+
+ if (process_event(&server, &event) < 0)
+ goto fail;
+ }
+
+ r = EXIT_SUCCESS;
+
+ log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
+
+fail:
+ sd_notify(false,
+ "STATUS=Shutting down...");
+
+ server_done(&server);
+
+ dbus_shutdown();
+
+ return r;
+}
diff --git a/src/modules-load.c b/src/modules-load.c
deleted file mode 100644
index 0f2144c..0000000
--- a/src/modules-load.c
+++ /dev/null
@@ -1,155 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <dirent.h>
-#include <libkmod.h>
-
-#include "log.h"
-#include "util.h"
-#include "strv.h"
-
-#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 main(int argc, char *argv[]) {
- int r = EXIT_FAILURE;
- char **files, **fn;
- struct kmod_ctx *ctx;
- const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST|KMOD_PROBE_IGNORE_LOADED;
-
- if (argc > 1) {
- log_error("This program takes no argument.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- ctx = kmod_new(NULL, NULL);
- if (!ctx) {
- log_error("Failed to allocate memory for kmod.");
- goto finish;
- }
-
- kmod_load_resources(ctx);
-
- kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
-
- if (conf_files_list(&files, ".conf",
- "/etc/modules-load.d",
- "/run/modules-load.d",
- "/usr/local/lib/modules-load.d",
- "/usr/lib/modules-load.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/modules-load.d",
-#endif
- NULL) < 0) {
- log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
- goto finish;
- }
-
- r = EXIT_SUCCESS;
-
- STRV_FOREACH(fn, files) {
- FILE *f;
-
- f = fopen(*fn, "re");
- if (!f) {
- if (errno == ENOENT)
- continue;
-
- log_error("Failed to open %s: %m", *fn);
- r = EXIT_FAILURE;
- continue;
- }
-
- log_debug("apply: %s\n", *fn);
- for (;;) {
- char line[LINE_MAX], *l;
- struct kmod_list *itr, *modlist = NULL;
- int err;
-
- if (!fgets(line, sizeof(line), f))
- break;
-
- l = strstrip(line);
- if (*l == '#' || *l == 0)
- continue;
-
- err = kmod_module_new_from_lookup(ctx, l, &modlist);
- if (err < 0) {
- log_error("Failed to lookup alias '%s'", l);
- r = EXIT_FAILURE;
- continue;
- }
-
- kmod_list_foreach(itr, modlist) {
- struct kmod_module *mod;
-
- mod = kmod_module_get_module(itr);
- err = kmod_module_probe_insert_module(mod, probe_flags,
- 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': %s", kmod_module_get_name(mod),
- strerror(-err));
- r = EXIT_FAILURE;
- }
-
- kmod_module_unref(mod);
- }
-
- kmod_module_unref_list(modlist);
- }
-
- if (ferror(f)) {
- log_error("Failed to read from file: %m");
- r = EXIT_FAILURE;
- }
-
- fclose(f);
- }
-
-finish:
- strv_free(files);
- kmod_unref(ctx);
-
- return r;
-}
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
new file mode 100644
index 0000000..0f2144c
--- /dev/null
+++ b/src/modules-load/modules-load.c
@@ -0,0 +1,155 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <dirent.h>
+#include <libkmod.h>
+
+#include "log.h"
+#include "util.h"
+#include "strv.h"
+
+#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 main(int argc, char *argv[]) {
+ int r = EXIT_FAILURE;
+ char **files, **fn;
+ struct kmod_ctx *ctx;
+ const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST|KMOD_PROBE_IGNORE_LOADED;
+
+ if (argc > 1) {
+ log_error("This program takes no argument.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ ctx = kmod_new(NULL, NULL);
+ if (!ctx) {
+ log_error("Failed to allocate memory for kmod.");
+ goto finish;
+ }
+
+ kmod_load_resources(ctx);
+
+ kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+
+ if (conf_files_list(&files, ".conf",
+ "/etc/modules-load.d",
+ "/run/modules-load.d",
+ "/usr/local/lib/modules-load.d",
+ "/usr/lib/modules-load.d",
+#ifdef HAVE_SPLIT_USR
+ "/lib/modules-load.d",
+#endif
+ NULL) < 0) {
+ log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = EXIT_SUCCESS;
+
+ STRV_FOREACH(fn, files) {
+ FILE *f;
+
+ f = fopen(*fn, "re");
+ if (!f) {
+ if (errno == ENOENT)
+ continue;
+
+ log_error("Failed to open %s: %m", *fn);
+ r = EXIT_FAILURE;
+ continue;
+ }
+
+ log_debug("apply: %s\n", *fn);
+ for (;;) {
+ char line[LINE_MAX], *l;
+ struct kmod_list *itr, *modlist = NULL;
+ int err;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ l = strstrip(line);
+ if (*l == '#' || *l == 0)
+ continue;
+
+ err = kmod_module_new_from_lookup(ctx, l, &modlist);
+ if (err < 0) {
+ log_error("Failed to lookup alias '%s'", l);
+ r = EXIT_FAILURE;
+ continue;
+ }
+
+ kmod_list_foreach(itr, modlist) {
+ struct kmod_module *mod;
+
+ mod = kmod_module_get_module(itr);
+ err = kmod_module_probe_insert_module(mod, probe_flags,
+ 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': %s", kmod_module_get_name(mod),
+ strerror(-err));
+ r = EXIT_FAILURE;
+ }
+
+ kmod_module_unref(mod);
+ }
+
+ kmod_module_unref_list(modlist);
+ }
+
+ if (ferror(f)) {
+ log_error("Failed to read from file: %m");
+ r = EXIT_FAILURE;
+ }
+
+ fclose(f);
+ }
+
+finish:
+ strv_free(files);
+ kmod_unref(ctx);
+
+ return r;
+}
diff --git a/src/notify.c b/src/notify.c
deleted file mode 100644
index ffc8dfe..0000000
--- a/src/notify.c
+++ /dev/null
@@ -1,228 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <getopt.h>
-#include <error.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <systemd/sd-daemon.h>
-
-#include "strv.h"
-#include "util.h"
-#include "log.h"
-#include "sd-readahead.h"
-#include "build.h"
-
-static bool arg_ready = false;
-static pid_t arg_pid = 0;
-static const char *arg_status = NULL;
-static bool arg_booted = false;
-static const char *arg_readahead = NULL;
-
-static int help(void) {
-
- printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
- "Notify the init system about service status updates.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " --ready Inform the init system about service start-up completion\n"
- " --pid[=PID] Set main pid of daemon\n"
- " --status=TEXT Set status text\n"
- " --booted Returns 0 if the system was booted up with systemd, non-zero otherwise\n"
- " --readahead=ACTION Controls read-ahead operations\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_READY = 0x100,
- ARG_VERSION,
- ARG_PID,
- ARG_STATUS,
- ARG_BOOTED,
- ARG_READAHEAD
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "ready", no_argument, NULL, ARG_READY },
- { "pid", optional_argument, NULL, ARG_PID },
- { "status", required_argument, NULL, ARG_STATUS },
- { "booted", no_argument, NULL, ARG_BOOTED },
- { "readahead", required_argument, NULL, ARG_READAHEAD },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(DISTRIBUTION);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case ARG_READY:
- arg_ready = true;
- break;
-
- case ARG_PID:
-
- if (optarg) {
- if (parse_pid(optarg, &arg_pid) < 0) {
- log_error("Failed to parse PID %s.", optarg);
- return -EINVAL;
- }
- } else
- arg_pid = getppid();
-
- break;
-
- case ARG_STATUS:
- arg_status = optarg;
- break;
-
- case ARG_BOOTED:
- arg_booted = true;
- break;
-
- case ARG_READAHEAD:
- arg_readahead = optarg;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind >= argc &&
- !arg_ready &&
- !arg_status &&
- !arg_pid &&
- !arg_booted &&
- !arg_readahead) {
- help();
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char* argv[]) {
- char* our_env[4], **final_env = NULL;
- unsigned i = 0;
- char *status = NULL, *cpid = NULL, *n = NULL;
- int r, retval = EXIT_FAILURE;
-
- log_parse_environment();
- log_open();
-
- if ((r = parse_argv(argc, argv)) <= 0) {
- retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
- goto finish;
- }
-
- if (arg_booted)
- return sd_booted() <= 0;
-
- if (arg_readahead) {
- if ((r = sd_readahead(arg_readahead)) < 0) {
- log_error("Failed to issue read-ahead control command: %s", strerror(-r));
- goto finish;
- }
- }
-
- if (arg_ready)
- our_env[i++] = (char*) "READY=1";
-
- if (arg_status) {
- if (!(status = strappend("STATUS=", arg_status))) {
- log_error("Failed to allocate STATUS string.");
- goto finish;
- }
-
- our_env[i++] = status;
- }
-
- if (arg_pid > 0) {
- if (asprintf(&cpid, "MAINPID=%lu", (unsigned long) arg_pid) < 0) {
- log_error("Failed to allocate MAINPID string.");
- goto finish;
- }
-
- our_env[i++] = cpid;
- }
-
- our_env[i++] = NULL;
-
- if (!(final_env = strv_env_merge(2, our_env, argv + optind))) {
- log_error("Failed to merge string sets.");
- goto finish;
- }
-
- if (strv_length(final_env) <= 0) {
- retval = EXIT_SUCCESS;
- goto finish;
- }
-
- if (!(n = strv_join(final_env, "\n"))) {
- log_error("Failed to concatenate strings.");
- goto finish;
- }
-
- if ((r = sd_notify(false, n)) < 0) {
- log_error("Failed to notify init system: %s", strerror(-r));
- goto finish;
- }
-
- retval = r <= 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-finish:
- free(status);
- free(cpid);
- free(n);
-
- strv_free(final_env);
-
- return retval;
-}
diff --git a/src/notify/notify.c b/src/notify/notify.c
new file mode 100644
index 0000000..ffc8dfe
--- /dev/null
+++ b/src/notify/notify.c
@@ -0,0 +1,228 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <getopt.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "strv.h"
+#include "util.h"
+#include "log.h"
+#include "sd-readahead.h"
+#include "build.h"
+
+static bool arg_ready = false;
+static pid_t arg_pid = 0;
+static const char *arg_status = NULL;
+static bool arg_booted = false;
+static const char *arg_readahead = NULL;
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
+ "Notify the init system about service status updates.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " --ready Inform the init system about service start-up completion\n"
+ " --pid[=PID] Set main pid of daemon\n"
+ " --status=TEXT Set status text\n"
+ " --booted Returns 0 if the system was booted up with systemd, non-zero otherwise\n"
+ " --readahead=ACTION Controls read-ahead operations\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_READY = 0x100,
+ ARG_VERSION,
+ ARG_PID,
+ ARG_STATUS,
+ ARG_BOOTED,
+ ARG_READAHEAD
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "ready", no_argument, NULL, ARG_READY },
+ { "pid", optional_argument, NULL, ARG_PID },
+ { "status", required_argument, NULL, ARG_STATUS },
+ { "booted", no_argument, NULL, ARG_BOOTED },
+ { "readahead", required_argument, NULL, ARG_READAHEAD },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(DISTRIBUTION);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case ARG_READY:
+ arg_ready = true;
+ break;
+
+ case ARG_PID:
+
+ if (optarg) {
+ if (parse_pid(optarg, &arg_pid) < 0) {
+ log_error("Failed to parse PID %s.", optarg);
+ return -EINVAL;
+ }
+ } else
+ arg_pid = getppid();
+
+ break;
+
+ case ARG_STATUS:
+ arg_status = optarg;
+ break;
+
+ case ARG_BOOTED:
+ arg_booted = true;
+ break;
+
+ case ARG_READAHEAD:
+ arg_readahead = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind >= argc &&
+ !arg_ready &&
+ !arg_status &&
+ !arg_pid &&
+ !arg_booted &&
+ !arg_readahead) {
+ help();
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char* argv[]) {
+ char* our_env[4], **final_env = NULL;
+ unsigned i = 0;
+ char *status = NULL, *cpid = NULL, *n = NULL;
+ int r, retval = EXIT_FAILURE;
+
+ log_parse_environment();
+ log_open();
+
+ if ((r = parse_argv(argc, argv)) <= 0) {
+ retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ goto finish;
+ }
+
+ if (arg_booted)
+ return sd_booted() <= 0;
+
+ if (arg_readahead) {
+ if ((r = sd_readahead(arg_readahead)) < 0) {
+ log_error("Failed to issue read-ahead control command: %s", strerror(-r));
+ goto finish;
+ }
+ }
+
+ if (arg_ready)
+ our_env[i++] = (char*) "READY=1";
+
+ if (arg_status) {
+ if (!(status = strappend("STATUS=", arg_status))) {
+ log_error("Failed to allocate STATUS string.");
+ goto finish;
+ }
+
+ our_env[i++] = status;
+ }
+
+ if (arg_pid > 0) {
+ if (asprintf(&cpid, "MAINPID=%lu", (unsigned long) arg_pid) < 0) {
+ log_error("Failed to allocate MAINPID string.");
+ goto finish;
+ }
+
+ our_env[i++] = cpid;
+ }
+
+ our_env[i++] = NULL;
+
+ if (!(final_env = strv_env_merge(2, our_env, argv + optind))) {
+ log_error("Failed to merge string sets.");
+ goto finish;
+ }
+
+ if (strv_length(final_env) <= 0) {
+ retval = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ if (!(n = strv_join(final_env, "\n"))) {
+ log_error("Failed to concatenate strings.");
+ goto finish;
+ }
+
+ if ((r = sd_notify(false, n)) < 0) {
+ log_error("Failed to notify init system: %s", strerror(-r));
+ goto finish;
+ }
+
+ retval = r <= 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+finish:
+ free(status);
+ free(cpid);
+ free(n);
+
+ strv_free(final_env);
+
+ return retval;
+}
diff --git a/src/nspawn.c b/src/nspawn.c
deleted file mode 100644
index 685b4d4..0000000
--- a/src/nspawn.c
+++ /dev/null
@@ -1,925 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <signal.h>
-#include <sched.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-#include <sys/mount.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#include <getopt.h>
-#include <sys/epoll.h>
-#include <termios.h>
-#include <sys/signalfd.h>
-#include <grp.h>
-#include <linux/fs.h>
-
-#include <systemd/sd-daemon.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "audit.h"
-#include "missing.h"
-#include "cgroup-util.h"
-#include "strv.h"
-#include "loopback-setup.h"
-
-static char *arg_directory = NULL;
-static char *arg_user = NULL;
-static char **arg_controllers = NULL;
-static bool arg_private_network = false;
-
-static int help(void) {
-
- printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
- "Spawn a minimal namespace container for debugging, testing and building.\n\n"
- " -h --help Show this help\n"
- " -D --directory=NAME Root directory for the container\n"
- " -u --user=USER Run the command under specified user or uid\n"
- " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
- " --private-network Disable network in container\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_PRIVATE_NETWORK = 0x100
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "directory", required_argument, NULL, 'D' },
- { "user", required_argument, NULL, 'u' },
- { "controllers", required_argument, NULL, 'C' },
- { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "+hD:u:C:", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case 'D':
- free(arg_directory);
- if (!(arg_directory = strdup(optarg))) {
- log_error("Failed to duplicate root directory.");
- return -ENOMEM;
- }
-
- break;
-
- case 'u':
- free(arg_user);
- if (!(arg_user = strdup(optarg))) {
- log_error("Failed to duplicate user name.");
- return -ENOMEM;
- }
-
- break;
-
- case 'C':
- strv_free(arg_controllers);
- arg_controllers = strv_split(optarg, ",");
- if (!arg_controllers) {
- log_error("Failed to split controllers list.");
- return -ENOMEM;
- }
- strv_uniq(arg_controllers);
-
- break;
-
- case ARG_PRIVATE_NETWORK:
- arg_private_network = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- return 1;
-}
-
-static int mount_all(const char *dest) {
-
- typedef struct MountPoint {
- const char *what;
- const char *where;
- const char *type;
- const char *options;
- unsigned long flags;
- bool fatal;
- } MountPoint;
-
- static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND, true }, /* Bind mount first */
- { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
- { "/sys", "/sys", "bind", NULL, MS_BIND, true }, /* Bind mount first */
- { "/sys", "/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
- { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
- { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND, true },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
-#ifdef HAVE_SELINUX
- { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND, false }, /* Bind mount first */
- { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
-#endif
- };
-
- unsigned k;
- int r = 0;
- char *where;
-
- for (k = 0; k < ELEMENTSOF(mount_table); k++) {
- int t;
-
- if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
- log_error("Out of memory");
-
- if (r == 0)
- r = -ENOMEM;
-
- break;
- }
-
- t = path_is_mount_point(where, false);
- if (t < 0) {
- log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t));
- free(where);
-
- if (r == 0)
- r = t;
-
- continue;
- }
-
- mkdir_p(where, 0755);
-
- if (mount(mount_table[k].what,
- where,
- mount_table[k].type,
- mount_table[k].flags,
- mount_table[k].options) < 0 &&
- mount_table[k].fatal) {
-
- log_error("mount(%s) failed: %m", where);
-
- if (r == 0)
- r = -errno;
- }
-
- free(where);
- }
-
- /* Fix the timezone, if possible */
- if (asprintf(&where, "%s/etc/localtime", dest) >= 0) {
-
- if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0)
- mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
-
- free(where);
- }
-
- if (asprintf(&where, "%s/etc/timezone", dest) >= 0) {
-
- if (mount("/etc/timezone", where, "bind", MS_BIND, NULL) >= 0)
- mount("/etc/timezone", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
-
- free(where);
- }
-
- if (asprintf(&where, "%s/proc/kmsg", dest) >= 0) {
- mount("/dev/null", where, "bind", MS_BIND, NULL);
- free(where);
- }
-
- return r;
-}
-
-static int copy_devnodes(const char *dest, const char *console) {
-
- static const char devnodes[] =
- "null\0"
- "zero\0"
- "full\0"
- "random\0"
- "urandom\0"
- "tty\0"
- "ptmx\0"
- "kmsg\0"
- "rtc0\0";
-
- const char *d;
- int r = 0, k;
- mode_t u;
- struct stat st;
- char *from = NULL, *to = NULL;
-
- assert(dest);
- assert(console);
-
- u = umask(0000);
-
- NULSTR_FOREACH(d, devnodes) {
- from = to = NULL;
-
- asprintf(&from, "/dev/%s", d);
- asprintf(&to, "%s/dev/%s", dest, d);
-
- if (!from || !to) {
- log_error("Failed to allocate devnode path");
-
- free(from);
- free(to);
-
- from = to = NULL;
-
- if (r == 0)
- r = -ENOMEM;
-
- break;
- }
-
- if (stat(from, &st) < 0) {
-
- if (errno != ENOENT) {
- log_error("Failed to stat %s: %m", from);
- if (r == 0)
- r = -errno;
- }
-
- } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
-
- log_error("%s is not a char or block device, cannot copy.", from);
- if (r == 0)
- r = -EIO;
-
- } else if (mknod(to, st.st_mode, st.st_rdev) < 0) {
-
- log_error("mknod(%s) failed: %m", dest);
- if (r == 0)
- r = -errno;
- }
-
- free(from);
- free(to);
- }
-
- if (stat(console, &st) < 0) {
-
- log_error("Failed to stat %s: %m", console);
- if (r == 0)
- r = -errno;
-
- goto finish;
-
- } else if (!S_ISCHR(st.st_mode)) {
-
- log_error("/dev/console is not a char device.");
- if (r == 0)
- r = -EIO;
-
- goto finish;
- }
-
- if (asprintf(&to, "%s/dev/console", dest) < 0) {
-
- log_error("Out of memory");
- if (r == 0)
- r = -ENOMEM;
-
- goto finish;
- }
-
- /* We need to bind mount the right tty to /dev/console since
- * ptys can only exist on pts file systems. To have something
- * to bind mount things on we create a device node first, that
- * has the right major/minor (note that the major minor
- * doesn't actually matter here, since we mount it over
- * anyway). */
-
- if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0)
- log_error("mknod for /dev/console failed: %m");
-
- if (mount(console, to, "bind", MS_BIND, NULL) < 0) {
- log_error("bind mount for /dev/console failed: %m");
-
- if (r == 0)
- r = -errno;
- }
-
- free(to);
-
- if ((k = chmod_and_chown(console, 0600, 0, 0)) < 0) {
- log_error("Failed to correct access mode for TTY: %s", strerror(-k));
-
- if (r == 0)
- r = k;
- }
-
-finish:
- umask(u);
-
- return r;
-}
-
-static int drop_capabilities(void) {
- static const unsigned long retain[] = {
- CAP_CHOWN,
- CAP_DAC_OVERRIDE,
- CAP_DAC_READ_SEARCH,
- CAP_FOWNER,
- CAP_FSETID,
- CAP_IPC_OWNER,
- CAP_KILL,
- CAP_LEASE,
- CAP_LINUX_IMMUTABLE,
- CAP_NET_BIND_SERVICE,
- CAP_NET_BROADCAST,
- CAP_NET_RAW,
- CAP_SETGID,
- CAP_SETFCAP,
- CAP_SETPCAP,
- CAP_SETUID,
- CAP_SYS_ADMIN,
- CAP_SYS_CHROOT,
- CAP_SYS_NICE,
- CAP_SYS_PTRACE,
- CAP_SYS_TTY_CONFIG
- };
-
- unsigned long l;
-
- for (l = 0; l <= cap_last_cap(); l++) {
- unsigned i;
-
- for (i = 0; i < ELEMENTSOF(retain); i++)
- if (retain[i] == l)
- break;
-
- if (i < ELEMENTSOF(retain))
- continue;
-
- if (prctl(PR_CAPBSET_DROP, l) < 0) {
- log_error("PR_CAPBSET_DROP failed: %m");
- return -errno;
- }
- }
-
- return 0;
-}
-
-static int is_os_tree(const char *path) {
- int r;
- char *p;
- /* We use /bin/sh as flag file if something is an OS */
-
- if (asprintf(&p, "%s/bin/sh", path) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- return r < 0 ? 0 : 1;
-}
-
-static int process_pty(int master, sigset_t *mask) {
-
- char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
- size_t in_buffer_full = 0, out_buffer_full = 0;
- struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
- bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
- int ep = -1, signal_fd = -1, r;
-
- fd_nonblock(STDIN_FILENO, 1);
- fd_nonblock(STDOUT_FILENO, 1);
- fd_nonblock(master, 1);
-
- if ((signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
- log_error("signalfd(): %m");
- r = -errno;
- goto finish;
- }
-
- if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
- log_error("Failed to create epoll: %m");
- r = -errno;
- goto finish;
- }
-
- zero(stdin_ev);
- stdin_ev.events = EPOLLIN|EPOLLET;
- stdin_ev.data.fd = STDIN_FILENO;
-
- zero(stdout_ev);
- stdout_ev.events = EPOLLOUT|EPOLLET;
- stdout_ev.data.fd = STDOUT_FILENO;
-
- zero(master_ev);
- master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
- master_ev.data.fd = master;
-
- zero(signal_ev);
- signal_ev.events = EPOLLIN;
- signal_ev.data.fd = signal_fd;
-
- if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
- epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
- log_error("Failed to regiser fds in epoll: %m");
- r = -errno;
- goto finish;
- }
-
- for (;;) {
- struct epoll_event ev[16];
- ssize_t k;
- int i, nfds;
-
- if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
-
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- log_error("epoll_wait(): %m");
- r = -errno;
- goto finish;
- }
-
- assert(nfds >= 1);
-
- for (i = 0; i < nfds; i++) {
- if (ev[i].data.fd == STDIN_FILENO) {
-
- if (ev[i].events & (EPOLLIN|EPOLLHUP))
- stdin_readable = true;
-
- } else if (ev[i].data.fd == STDOUT_FILENO) {
-
- if (ev[i].events & (EPOLLOUT|EPOLLHUP))
- stdout_writable = true;
-
- } else if (ev[i].data.fd == master) {
-
- if (ev[i].events & (EPOLLIN|EPOLLHUP))
- master_readable = true;
-
- if (ev[i].events & (EPOLLOUT|EPOLLHUP))
- master_writable = true;
-
- } else if (ev[i].data.fd == signal_fd) {
- struct signalfd_siginfo sfsi;
- ssize_t n;
-
- if ((n = read(signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
-
- if (n >= 0) {
- log_error("Failed to read from signalfd: invalid block size");
- r = -EIO;
- goto finish;
- }
-
- if (errno != EINTR && errno != EAGAIN) {
- log_error("Failed to read from signalfd: %m");
- r = -errno;
- goto finish;
- }
- } else {
-
- if (sfsi.ssi_signo == SIGWINCH) {
- struct winsize ws;
-
- /* The window size changed, let's forward that. */
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
- ioctl(master, TIOCSWINSZ, &ws);
- } else {
- r = 0;
- goto finish;
- }
- }
- }
- }
-
- while ((stdin_readable && in_buffer_full <= 0) ||
- (master_writable && in_buffer_full > 0) ||
- (master_readable && out_buffer_full <= 0) ||
- (stdout_writable && out_buffer_full > 0)) {
-
- if (stdin_readable && in_buffer_full < LINE_MAX) {
-
- if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full)) < 0) {
-
- if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
- stdin_readable = false;
- else {
- log_error("read(): %m");
- r = -errno;
- goto finish;
- }
- } else
- in_buffer_full += (size_t) k;
- }
-
- if (master_writable && in_buffer_full > 0) {
-
- if ((k = write(master, in_buffer, in_buffer_full)) < 0) {
-
- if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
- master_writable = false;
- else {
- log_error("write(): %m");
- r = -errno;
- goto finish;
- }
-
- } else {
- assert(in_buffer_full >= (size_t) k);
- memmove(in_buffer, in_buffer + k, in_buffer_full - k);
- in_buffer_full -= k;
- }
- }
-
- if (master_readable && out_buffer_full < LINE_MAX) {
-
- if ((k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full)) < 0) {
-
- if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
- master_readable = false;
- else {
- log_error("read(): %m");
- r = -errno;
- goto finish;
- }
- } else
- out_buffer_full += (size_t) k;
- }
-
- if (stdout_writable && out_buffer_full > 0) {
-
- if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
-
- if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
- stdout_writable = false;
- else {
- log_error("write(): %m");
- r = -errno;
- goto finish;
- }
-
- } else {
- assert(out_buffer_full >= (size_t) k);
- memmove(out_buffer, out_buffer + k, out_buffer_full - k);
- out_buffer_full -= k;
- }
- }
- }
- }
-
-finish:
- if (ep >= 0)
- close_nointr_nofail(ep);
-
- if (signal_fd >= 0)
- close_nointr_nofail(signal_fd);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- pid_t pid = 0;
- int r = EXIT_FAILURE, k;
- char *oldcg = NULL, *newcg = NULL;
- char **controller = NULL;
- int master = -1;
- const char *console = NULL;
- struct termios saved_attr, raw_attr;
- sigset_t mask;
- bool saved_attr_valid = false;
- struct winsize ws;
-
- log_parse_environment();
- log_open();
-
- if ((r = parse_argv(argc, argv)) <= 0)
- goto finish;
-
- if (arg_directory) {
- char *p;
-
- p = path_make_absolute_cwd(arg_directory);
- free(arg_directory);
- arg_directory = p;
- } else
- arg_directory = get_current_dir_name();
-
- if (!arg_directory) {
- log_error("Failed to determine path");
- goto finish;
- }
-
- path_kill_slashes(arg_directory);
-
- if (geteuid() != 0) {
- log_error("Need to be root.");
- goto finish;
- }
-
- if (sd_booted() <= 0) {
- log_error("Not running on a systemd system.");
- goto finish;
- }
-
- if (path_equal(arg_directory, "/")) {
- log_error("Spawning container on root directory not supported.");
- goto finish;
- }
-
- if (is_os_tree(arg_directory) <= 0) {
- log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory);
- goto finish;
- }
-
- if ((k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg)) < 0) {
- log_error("Failed to determine current cgroup: %s", strerror(-k));
- goto finish;
- }
-
- if (asprintf(&newcg, "%s/nspawn-%lu", oldcg, (unsigned long) getpid()) < 0) {
- log_error("Failed to allocate cgroup path.");
- goto finish;
- }
-
- k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
- if (k < 0) {
- log_error("Failed to create cgroup: %s", strerror(-k));
- goto finish;
- }
-
- STRV_FOREACH(controller,arg_controllers) {
- k = cg_create_and_attach(*controller, newcg, 0);
- if (k < 0)
- log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
- }
-
- if ((master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY)) < 0) {
- log_error("Failed to acquire pseudo tty: %m");
- goto finish;
- }
-
- if (!(console = ptsname(master))) {
- log_error("Failed to determine tty name: %m");
- goto finish;
- }
-
- log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
-
- if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
- ioctl(master, TIOCSWINSZ, &ws);
-
- if (unlockpt(master) < 0) {
- log_error("Failed to unlock tty: %m");
- goto finish;
- }
-
- if (tcgetattr(STDIN_FILENO, &saved_attr) < 0) {
- log_error("Failed to get terminal attributes: %m");
- goto finish;
- }
-
- saved_attr_valid = true;
-
- raw_attr = saved_attr;
- cfmakeraw(&raw_attr);
- raw_attr.c_lflag &= ~ECHO;
-
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
- log_error("Failed to set terminal attributes: %m");
- goto finish;
- }
-
- assert_se(sigemptyset(&mask) == 0);
- sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
- assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
-
- pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
- if (pid < 0) {
- if (errno == EINVAL)
- log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
- else
- log_error("clone() failed: %m");
-
- goto finish;
- }
-
- if (pid == 0) {
- /* child */
-
- const char *hn;
- const char *home = NULL;
- uid_t uid = (uid_t) -1;
- gid_t gid = (gid_t) -1;
- const char *envp[] = {
- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
- "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
- NULL, /* TERM */
- NULL, /* HOME */
- NULL, /* USER */
- NULL, /* LOGNAME */
- NULL
- };
-
- envp[2] = strv_find_prefix(environ, "TERM=");
-
- close_nointr_nofail(master);
-
- close_nointr(STDIN_FILENO);
- close_nointr(STDOUT_FILENO);
- close_nointr(STDERR_FILENO);
-
- close_all_fds(NULL, 0);
-
- reset_all_signal_handlers();
-
- assert_se(sigemptyset(&mask) == 0);
- assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
- if (setsid() < 0)
- goto child_fail;
-
- if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
- goto child_fail;
-
- /* Mark / as private, in case somebody marked it shared */
- if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
- goto child_fail;
-
- if (mount_all(arg_directory) < 0)
- goto child_fail;
-
- if (copy_devnodes(arg_directory, console) < 0)
- goto child_fail;
-
- if (chdir(arg_directory) < 0) {
- log_error("chdir(%s) failed: %m", arg_directory);
- goto child_fail;
- }
-
- if (open_terminal("dev/console", O_RDWR) != STDIN_FILENO ||
- dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
- dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
- goto child_fail;
-
- if (mount(arg_directory, "/", "bind", MS_BIND|MS_MOVE, NULL) < 0) {
- log_error("mount(MS_MOVE) failed: %m");
- goto child_fail;
- }
-
- if (chroot(".") < 0) {
- log_error("chroot() failed: %m");
- goto child_fail;
- }
-
- if (chdir("/") < 0) {
- log_error("chdir() failed: %m");
- goto child_fail;
- }
-
- umask(0022);
-
- loopback_setup();
-
- if (drop_capabilities() < 0)
- goto child_fail;
-
- if (arg_user) {
-
- if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
- log_error("get_user_creds() failed: %m");
- goto child_fail;
- }
-
- if (mkdir_parents(home, 0775) < 0) {
- log_error("mkdir_parents() failed: %m");
- goto child_fail;
- }
-
- if (safe_mkdir(home, 0775, uid, gid) < 0) {
- log_error("safe_mkdir() failed: %m");
- goto child_fail;
- }
-
- if (initgroups((const char*)arg_user, gid) < 0) {
- log_error("initgroups() failed: %m");
- goto child_fail;
- }
-
- if (setresgid(gid, gid, gid) < 0) {
- log_error("setregid() failed: %m");
- goto child_fail;
- }
-
- if (setresuid(uid, uid, uid) < 0) {
- log_error("setreuid() failed: %m");
- goto child_fail;
- }
- }
-
- if ((asprintf((char**)(envp + 3), "HOME=%s", home? home: "/root") < 0) ||
- (asprintf((char**)(envp + 4), "USER=%s", arg_user? arg_user : "root") < 0) ||
- (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
- log_error("Out of memory");
- goto child_fail;
- }
-
- if ((hn = file_name_from_path(arg_directory)))
- sethostname(hn, strlen(hn));
-
- if (argc > optind)
- execvpe(argv[optind], argv + optind, (char**) envp);
- else {
- chdir(home ? home : "/root");
- execle("/bin/bash", "-bash", NULL, (char**) envp);
- }
-
- log_error("execv() failed: %m");
-
- child_fail:
- _exit(EXIT_FAILURE);
- }
-
- if (process_pty(master, &mask) < 0)
- goto finish;
-
- if (saved_attr_valid) {
- tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
- saved_attr_valid = false;
- }
-
- r = wait_for_terminate_and_warn(argc > optind ? argv[optind] : "bash", pid);
-
- if (r < 0)
- r = EXIT_FAILURE;
-
-finish:
- if (saved_attr_valid)
- tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
-
- if (master >= 0)
- close_nointr_nofail(master);
-
- if (oldcg)
- cg_attach(SYSTEMD_CGROUP_CONTROLLER, oldcg, 0);
-
- if (newcg)
- cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
-
- free(arg_directory);
- strv_free(arg_controllers);
- free(oldcg);
- free(newcg);
-
- return r;
-}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
new file mode 100644
index 0000000..685b4d4
--- /dev/null
+++ b/src/nspawn/nspawn.c
@@ -0,0 +1,925 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <signal.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <getopt.h>
+#include <sys/epoll.h>
+#include <termios.h>
+#include <sys/signalfd.h>
+#include <grp.h>
+#include <linux/fs.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "audit.h"
+#include "missing.h"
+#include "cgroup-util.h"
+#include "strv.h"
+#include "loopback-setup.h"
+
+static char *arg_directory = NULL;
+static char *arg_user = NULL;
+static char **arg_controllers = NULL;
+static bool arg_private_network = false;
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
+ "Spawn a minimal namespace container for debugging, testing and building.\n\n"
+ " -h --help Show this help\n"
+ " -D --directory=NAME Root directory for the container\n"
+ " -u --user=USER Run the command under specified user or uid\n"
+ " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
+ " --private-network Disable network in container\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_PRIVATE_NETWORK = 0x100
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "directory", required_argument, NULL, 'D' },
+ { "user", required_argument, NULL, 'u' },
+ { "controllers", required_argument, NULL, 'C' },
+ { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+hD:u:C:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case 'D':
+ free(arg_directory);
+ if (!(arg_directory = strdup(optarg))) {
+ log_error("Failed to duplicate root directory.");
+ return -ENOMEM;
+ }
+
+ break;
+
+ case 'u':
+ free(arg_user);
+ if (!(arg_user = strdup(optarg))) {
+ log_error("Failed to duplicate user name.");
+ return -ENOMEM;
+ }
+
+ break;
+
+ case 'C':
+ strv_free(arg_controllers);
+ arg_controllers = strv_split(optarg, ",");
+ if (!arg_controllers) {
+ log_error("Failed to split controllers list.");
+ return -ENOMEM;
+ }
+ strv_uniq(arg_controllers);
+
+ break;
+
+ case ARG_PRIVATE_NETWORK:
+ arg_private_network = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+static int mount_all(const char *dest) {
+
+ typedef struct MountPoint {
+ const char *what;
+ const char *where;
+ const char *type;
+ const char *options;
+ unsigned long flags;
+ bool fatal;
+ } MountPoint;
+
+ static const MountPoint mount_table[] = {
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
+ { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND, true }, /* Bind mount first */
+ { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
+ { "/sys", "/sys", "bind", NULL, MS_BIND, true }, /* Bind mount first */
+ { "/sys", "/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
+ { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND, true },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
+#ifdef HAVE_SELINUX
+ { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND, false }, /* Bind mount first */
+ { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
+#endif
+ };
+
+ unsigned k;
+ int r = 0;
+ char *where;
+
+ for (k = 0; k < ELEMENTSOF(mount_table); k++) {
+ int t;
+
+ if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
+ log_error("Out of memory");
+
+ if (r == 0)
+ r = -ENOMEM;
+
+ break;
+ }
+
+ t = path_is_mount_point(where, false);
+ if (t < 0) {
+ log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t));
+ free(where);
+
+ if (r == 0)
+ r = t;
+
+ continue;
+ }
+
+ mkdir_p(where, 0755);
+
+ if (mount(mount_table[k].what,
+ where,
+ mount_table[k].type,
+ mount_table[k].flags,
+ mount_table[k].options) < 0 &&
+ mount_table[k].fatal) {
+
+ log_error("mount(%s) failed: %m", where);
+
+ if (r == 0)
+ r = -errno;
+ }
+
+ free(where);
+ }
+
+ /* Fix the timezone, if possible */
+ if (asprintf(&where, "%s/etc/localtime", dest) >= 0) {
+
+ if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0)
+ mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+
+ free(where);
+ }
+
+ if (asprintf(&where, "%s/etc/timezone", dest) >= 0) {
+
+ if (mount("/etc/timezone", where, "bind", MS_BIND, NULL) >= 0)
+ mount("/etc/timezone", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+
+ free(where);
+ }
+
+ if (asprintf(&where, "%s/proc/kmsg", dest) >= 0) {
+ mount("/dev/null", where, "bind", MS_BIND, NULL);
+ free(where);
+ }
+
+ return r;
+}
+
+static int copy_devnodes(const char *dest, const char *console) {
+
+ static const char devnodes[] =
+ "null\0"
+ "zero\0"
+ "full\0"
+ "random\0"
+ "urandom\0"
+ "tty\0"
+ "ptmx\0"
+ "kmsg\0"
+ "rtc0\0";
+
+ const char *d;
+ int r = 0, k;
+ mode_t u;
+ struct stat st;
+ char *from = NULL, *to = NULL;
+
+ assert(dest);
+ assert(console);
+
+ u = umask(0000);
+
+ NULSTR_FOREACH(d, devnodes) {
+ from = to = NULL;
+
+ asprintf(&from, "/dev/%s", d);
+ asprintf(&to, "%s/dev/%s", dest, d);
+
+ if (!from || !to) {
+ log_error("Failed to allocate devnode path");
+
+ free(from);
+ free(to);
+
+ from = to = NULL;
+
+ if (r == 0)
+ r = -ENOMEM;
+
+ break;
+ }
+
+ if (stat(from, &st) < 0) {
+
+ if (errno != ENOENT) {
+ log_error("Failed to stat %s: %m", from);
+ if (r == 0)
+ r = -errno;
+ }
+
+ } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+
+ log_error("%s is not a char or block device, cannot copy.", from);
+ if (r == 0)
+ r = -EIO;
+
+ } else if (mknod(to, st.st_mode, st.st_rdev) < 0) {
+
+ log_error("mknod(%s) failed: %m", dest);
+ if (r == 0)
+ r = -errno;
+ }
+
+ free(from);
+ free(to);
+ }
+
+ if (stat(console, &st) < 0) {
+
+ log_error("Failed to stat %s: %m", console);
+ if (r == 0)
+ r = -errno;
+
+ goto finish;
+
+ } else if (!S_ISCHR(st.st_mode)) {
+
+ log_error("/dev/console is not a char device.");
+ if (r == 0)
+ r = -EIO;
+
+ goto finish;
+ }
+
+ if (asprintf(&to, "%s/dev/console", dest) < 0) {
+
+ log_error("Out of memory");
+ if (r == 0)
+ r = -ENOMEM;
+
+ goto finish;
+ }
+
+ /* We need to bind mount the right tty to /dev/console since
+ * ptys can only exist on pts file systems. To have something
+ * to bind mount things on we create a device node first, that
+ * has the right major/minor (note that the major minor
+ * doesn't actually matter here, since we mount it over
+ * anyway). */
+
+ if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0)
+ log_error("mknod for /dev/console failed: %m");
+
+ if (mount(console, to, "bind", MS_BIND, NULL) < 0) {
+ log_error("bind mount for /dev/console failed: %m");
+
+ if (r == 0)
+ r = -errno;
+ }
+
+ free(to);
+
+ if ((k = chmod_and_chown(console, 0600, 0, 0)) < 0) {
+ log_error("Failed to correct access mode for TTY: %s", strerror(-k));
+
+ if (r == 0)
+ r = k;
+ }
+
+finish:
+ umask(u);
+
+ return r;
+}
+
+static int drop_capabilities(void) {
+ static const unsigned long retain[] = {
+ CAP_CHOWN,
+ CAP_DAC_OVERRIDE,
+ CAP_DAC_READ_SEARCH,
+ CAP_FOWNER,
+ CAP_FSETID,
+ CAP_IPC_OWNER,
+ CAP_KILL,
+ CAP_LEASE,
+ CAP_LINUX_IMMUTABLE,
+ CAP_NET_BIND_SERVICE,
+ CAP_NET_BROADCAST,
+ CAP_NET_RAW,
+ CAP_SETGID,
+ CAP_SETFCAP,
+ CAP_SETPCAP,
+ CAP_SETUID,
+ CAP_SYS_ADMIN,
+ CAP_SYS_CHROOT,
+ CAP_SYS_NICE,
+ CAP_SYS_PTRACE,
+ CAP_SYS_TTY_CONFIG
+ };
+
+ unsigned long l;
+
+ for (l = 0; l <= cap_last_cap(); l++) {
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(retain); i++)
+ if (retain[i] == l)
+ break;
+
+ if (i < ELEMENTSOF(retain))
+ continue;
+
+ if (prctl(PR_CAPBSET_DROP, l) < 0) {
+ log_error("PR_CAPBSET_DROP failed: %m");
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+static int is_os_tree(const char *path) {
+ int r;
+ char *p;
+ /* We use /bin/sh as flag file if something is an OS */
+
+ if (asprintf(&p, "%s/bin/sh", path) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ return r < 0 ? 0 : 1;
+}
+
+static int process_pty(int master, sigset_t *mask) {
+
+ char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
+ size_t in_buffer_full = 0, out_buffer_full = 0;
+ struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
+ bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
+ int ep = -1, signal_fd = -1, r;
+
+ fd_nonblock(STDIN_FILENO, 1);
+ fd_nonblock(STDOUT_FILENO, 1);
+ fd_nonblock(master, 1);
+
+ if ((signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+ log_error("signalfd(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ log_error("Failed to create epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ zero(stdin_ev);
+ stdin_ev.events = EPOLLIN|EPOLLET;
+ stdin_ev.data.fd = STDIN_FILENO;
+
+ zero(stdout_ev);
+ stdout_ev.events = EPOLLOUT|EPOLLET;
+ stdout_ev.data.fd = STDOUT_FILENO;
+
+ zero(master_ev);
+ master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
+ master_ev.data.fd = master;
+
+ zero(signal_ev);
+ signal_ev.events = EPOLLIN;
+ signal_ev.data.fd = signal_fd;
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
+ epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
+ log_error("Failed to regiser fds in epoll: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ for (;;) {
+ struct epoll_event ev[16];
+ ssize_t k;
+ int i, nfds;
+
+ if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ log_error("epoll_wait(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ assert(nfds >= 1);
+
+ for (i = 0; i < nfds; i++) {
+ if (ev[i].data.fd == STDIN_FILENO) {
+
+ if (ev[i].events & (EPOLLIN|EPOLLHUP))
+ stdin_readable = true;
+
+ } else if (ev[i].data.fd == STDOUT_FILENO) {
+
+ if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+ stdout_writable = true;
+
+ } else if (ev[i].data.fd == master) {
+
+ if (ev[i].events & (EPOLLIN|EPOLLHUP))
+ master_readable = true;
+
+ if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+ master_writable = true;
+
+ } else if (ev[i].data.fd == signal_fd) {
+ struct signalfd_siginfo sfsi;
+ ssize_t n;
+
+ if ((n = read(signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+
+ if (n >= 0) {
+ log_error("Failed to read from signalfd: invalid block size");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (errno != EINTR && errno != EAGAIN) {
+ log_error("Failed to read from signalfd: %m");
+ r = -errno;
+ goto finish;
+ }
+ } else {
+
+ if (sfsi.ssi_signo == SIGWINCH) {
+ struct winsize ws;
+
+ /* The window size changed, let's forward that. */
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+ ioctl(master, TIOCSWINSZ, &ws);
+ } else {
+ r = 0;
+ goto finish;
+ }
+ }
+ }
+ }
+
+ while ((stdin_readable && in_buffer_full <= 0) ||
+ (master_writable && in_buffer_full > 0) ||
+ (master_readable && out_buffer_full <= 0) ||
+ (stdout_writable && out_buffer_full > 0)) {
+
+ if (stdin_readable && in_buffer_full < LINE_MAX) {
+
+ if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ stdin_readable = false;
+ else {
+ log_error("read(): %m");
+ r = -errno;
+ goto finish;
+ }
+ } else
+ in_buffer_full += (size_t) k;
+ }
+
+ if (master_writable && in_buffer_full > 0) {
+
+ if ((k = write(master, in_buffer, in_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ master_writable = false;
+ else {
+ log_error("write(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ } else {
+ assert(in_buffer_full >= (size_t) k);
+ memmove(in_buffer, in_buffer + k, in_buffer_full - k);
+ in_buffer_full -= k;
+ }
+ }
+
+ if (master_readable && out_buffer_full < LINE_MAX) {
+
+ if ((k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ master_readable = false;
+ else {
+ log_error("read(): %m");
+ r = -errno;
+ goto finish;
+ }
+ } else
+ out_buffer_full += (size_t) k;
+ }
+
+ if (stdout_writable && out_buffer_full > 0) {
+
+ if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
+
+ if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+ stdout_writable = false;
+ else {
+ log_error("write(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ } else {
+ assert(out_buffer_full >= (size_t) k);
+ memmove(out_buffer, out_buffer + k, out_buffer_full - k);
+ out_buffer_full -= k;
+ }
+ }
+ }
+ }
+
+finish:
+ if (ep >= 0)
+ close_nointr_nofail(ep);
+
+ if (signal_fd >= 0)
+ close_nointr_nofail(signal_fd);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ pid_t pid = 0;
+ int r = EXIT_FAILURE, k;
+ char *oldcg = NULL, *newcg = NULL;
+ char **controller = NULL;
+ int master = -1;
+ const char *console = NULL;
+ struct termios saved_attr, raw_attr;
+ sigset_t mask;
+ bool saved_attr_valid = false;
+ struct winsize ws;
+
+ log_parse_environment();
+ log_open();
+
+ if ((r = parse_argv(argc, argv)) <= 0)
+ goto finish;
+
+ if (arg_directory) {
+ char *p;
+
+ p = path_make_absolute_cwd(arg_directory);
+ free(arg_directory);
+ arg_directory = p;
+ } else
+ arg_directory = get_current_dir_name();
+
+ if (!arg_directory) {
+ log_error("Failed to determine path");
+ goto finish;
+ }
+
+ path_kill_slashes(arg_directory);
+
+ if (geteuid() != 0) {
+ log_error("Need to be root.");
+ goto finish;
+ }
+
+ if (sd_booted() <= 0) {
+ log_error("Not running on a systemd system.");
+ goto finish;
+ }
+
+ if (path_equal(arg_directory, "/")) {
+ log_error("Spawning container on root directory not supported.");
+ goto finish;
+ }
+
+ if (is_os_tree(arg_directory) <= 0) {
+ log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory);
+ goto finish;
+ }
+
+ if ((k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg)) < 0) {
+ log_error("Failed to determine current cgroup: %s", strerror(-k));
+ goto finish;
+ }
+
+ if (asprintf(&newcg, "%s/nspawn-%lu", oldcg, (unsigned long) getpid()) < 0) {
+ log_error("Failed to allocate cgroup path.");
+ goto finish;
+ }
+
+ k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
+ if (k < 0) {
+ log_error("Failed to create cgroup: %s", strerror(-k));
+ goto finish;
+ }
+
+ STRV_FOREACH(controller,arg_controllers) {
+ k = cg_create_and_attach(*controller, newcg, 0);
+ if (k < 0)
+ log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
+ }
+
+ if ((master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY)) < 0) {
+ log_error("Failed to acquire pseudo tty: %m");
+ goto finish;
+ }
+
+ if (!(console = ptsname(master))) {
+ log_error("Failed to determine tty name: %m");
+ goto finish;
+ }
+
+ log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+ ioctl(master, TIOCSWINSZ, &ws);
+
+ if (unlockpt(master) < 0) {
+ log_error("Failed to unlock tty: %m");
+ goto finish;
+ }
+
+ if (tcgetattr(STDIN_FILENO, &saved_attr) < 0) {
+ log_error("Failed to get terminal attributes: %m");
+ goto finish;
+ }
+
+ saved_attr_valid = true;
+
+ raw_attr = saved_attr;
+ cfmakeraw(&raw_attr);
+ raw_attr.c_lflag &= ~ECHO;
+
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
+ log_error("Failed to set terminal attributes: %m");
+ goto finish;
+ }
+
+ assert_se(sigemptyset(&mask) == 0);
+ sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
+ assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
+
+ pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
+ if (pid < 0) {
+ if (errno == EINVAL)
+ log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
+ else
+ log_error("clone() failed: %m");
+
+ goto finish;
+ }
+
+ if (pid == 0) {
+ /* child */
+
+ const char *hn;
+ const char *home = NULL;
+ uid_t uid = (uid_t) -1;
+ gid_t gid = (gid_t) -1;
+ const char *envp[] = {
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+ "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
+ NULL, /* TERM */
+ NULL, /* HOME */
+ NULL, /* USER */
+ NULL, /* LOGNAME */
+ NULL
+ };
+
+ envp[2] = strv_find_prefix(environ, "TERM=");
+
+ close_nointr_nofail(master);
+
+ close_nointr(STDIN_FILENO);
+ close_nointr(STDOUT_FILENO);
+ close_nointr(STDERR_FILENO);
+
+ close_all_fds(NULL, 0);
+
+ reset_all_signal_handlers();
+
+ assert_se(sigemptyset(&mask) == 0);
+ assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+ if (setsid() < 0)
+ goto child_fail;
+
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
+ goto child_fail;
+
+ /* Mark / as private, in case somebody marked it shared */
+ if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
+ goto child_fail;
+
+ if (mount_all(arg_directory) < 0)
+ goto child_fail;
+
+ if (copy_devnodes(arg_directory, console) < 0)
+ goto child_fail;
+
+ if (chdir(arg_directory) < 0) {
+ log_error("chdir(%s) failed: %m", arg_directory);
+ goto child_fail;
+ }
+
+ if (open_terminal("dev/console", O_RDWR) != STDIN_FILENO ||
+ dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
+ dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
+ goto child_fail;
+
+ if (mount(arg_directory, "/", "bind", MS_BIND|MS_MOVE, NULL) < 0) {
+ log_error("mount(MS_MOVE) failed: %m");
+ goto child_fail;
+ }
+
+ if (chroot(".") < 0) {
+ log_error("chroot() failed: %m");
+ goto child_fail;
+ }
+
+ if (chdir("/") < 0) {
+ log_error("chdir() failed: %m");
+ goto child_fail;
+ }
+
+ umask(0022);
+
+ loopback_setup();
+
+ if (drop_capabilities() < 0)
+ goto child_fail;
+
+ if (arg_user) {
+
+ if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
+ log_error("get_user_creds() failed: %m");
+ goto child_fail;
+ }
+
+ if (mkdir_parents(home, 0775) < 0) {
+ log_error("mkdir_parents() failed: %m");
+ goto child_fail;
+ }
+
+ if (safe_mkdir(home, 0775, uid, gid) < 0) {
+ log_error("safe_mkdir() failed: %m");
+ goto child_fail;
+ }
+
+ if (initgroups((const char*)arg_user, gid) < 0) {
+ log_error("initgroups() failed: %m");
+ goto child_fail;
+ }
+
+ if (setresgid(gid, gid, gid) < 0) {
+ log_error("setregid() failed: %m");
+ goto child_fail;
+ }
+
+ if (setresuid(uid, uid, uid) < 0) {
+ log_error("setreuid() failed: %m");
+ goto child_fail;
+ }
+ }
+
+ if ((asprintf((char**)(envp + 3), "HOME=%s", home? home: "/root") < 0) ||
+ (asprintf((char**)(envp + 4), "USER=%s", arg_user? arg_user : "root") < 0) ||
+ (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
+ log_error("Out of memory");
+ goto child_fail;
+ }
+
+ if ((hn = file_name_from_path(arg_directory)))
+ sethostname(hn, strlen(hn));
+
+ if (argc > optind)
+ execvpe(argv[optind], argv + optind, (char**) envp);
+ else {
+ chdir(home ? home : "/root");
+ execle("/bin/bash", "-bash", NULL, (char**) envp);
+ }
+
+ log_error("execv() failed: %m");
+
+ child_fail:
+ _exit(EXIT_FAILURE);
+ }
+
+ if (process_pty(master, &mask) < 0)
+ goto finish;
+
+ if (saved_attr_valid) {
+ tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
+ saved_attr_valid = false;
+ }
+
+ r = wait_for_terminate_and_warn(argc > optind ? argv[optind] : "bash", pid);
+
+ if (r < 0)
+ r = EXIT_FAILURE;
+
+finish:
+ if (saved_attr_valid)
+ tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
+
+ if (master >= 0)
+ close_nointr_nofail(master);
+
+ if (oldcg)
+ cg_attach(SYSTEMD_CGROUP_CONTROLLER, oldcg, 0);
+
+ if (newcg)
+ cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
+
+ free(arg_directory);
+ strv_free(arg_controllers);
+ free(oldcg);
+ free(newcg);
+
+ return r;
+}
diff --git a/src/quotacheck.c b/src/quotacheck.c
deleted file mode 100644
index e4420ee..0000000
--- a/src/quotacheck.c
+++ /dev/null
@@ -1,120 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "virt.h"
-
-static bool arg_skip = false;
-static bool arg_force = false;
-
-static int parse_proc_cmdline(void) {
- char *line, *w, *state;
- int r;
- size_t l;
-
- if (detect_container(NULL) > 0)
- return 0;
-
- if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
- log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
- return 0;
- }
-
- FOREACH_WORD_QUOTED(w, l, line, state) {
-
- if (strneq(w, "quotacheck.mode=auto", l))
- arg_force = arg_skip = false;
- else if (strneq(w, "quotacheck.mode=force", l))
- arg_force = true;
- else if (strneq(w, "quotacheck.mode=skip", l))
- arg_skip = true;
- else if (startswith(w, "quotacheck.mode"))
- log_warning("Invalid quotacheck.mode= parameter. Ignoring.");
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
- else if (strneq(w, "forcequotacheck", l))
- arg_force = true;
-#endif
- }
-
- free(line);
- return 0;
-}
-
-static void test_files(void) {
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
- /* This exists only on Fedora, Mandriva or Mageia */
- if (access("/forcequotacheck", F_OK) >= 0)
- arg_force = true;
-#endif
-}
-
-int main(int argc, char *argv[]) {
- static const char * const cmdline[] = {
- "/sbin/quotacheck",
- "-anug",
- NULL
- };
-
- int r = EXIT_FAILURE;
- pid_t pid;
-
- if (argc > 1) {
- log_error("This program takes no arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- parse_proc_cmdline();
- test_files();
-
- if (!arg_force) {
- if (arg_skip)
- return 0;
-
- if (access("/run/systemd/quotacheck", F_OK) < 0)
- return 0;
- }
-
- if ((pid = fork()) < 0) {
- log_error("fork(): %m");
- goto finish;
- } else if (pid == 0) {
- /* Child */
- execv(cmdline[0], (char**) cmdline);
- _exit(1); /* Operational error */
- }
-
- r = wait_for_terminate_and_warn("quotacheck", pid) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-
-finish:
- return r;
-}
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
new file mode 100644
index 0000000..e4420ee
--- /dev/null
+++ b/src/quotacheck/quotacheck.c
@@ -0,0 +1,120 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "virt.h"
+
+static bool arg_skip = false;
+static bool arg_force = false;
+
+static int parse_proc_cmdline(void) {
+ char *line, *w, *state;
+ int r;
+ size_t l;
+
+ if (detect_container(NULL) > 0)
+ return 0;
+
+ if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+ return 0;
+ }
+
+ FOREACH_WORD_QUOTED(w, l, line, state) {
+
+ if (strneq(w, "quotacheck.mode=auto", l))
+ arg_force = arg_skip = false;
+ else if (strneq(w, "quotacheck.mode=force", l))
+ arg_force = true;
+ else if (strneq(w, "quotacheck.mode=skip", l))
+ arg_skip = true;
+ else if (startswith(w, "quotacheck.mode"))
+ log_warning("Invalid quotacheck.mode= parameter. Ignoring.");
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+ else if (strneq(w, "forcequotacheck", l))
+ arg_force = true;
+#endif
+ }
+
+ free(line);
+ return 0;
+}
+
+static void test_files(void) {
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+ /* This exists only on Fedora, Mandriva or Mageia */
+ if (access("/forcequotacheck", F_OK) >= 0)
+ arg_force = true;
+#endif
+}
+
+int main(int argc, char *argv[]) {
+ static const char * const cmdline[] = {
+ "/sbin/quotacheck",
+ "-anug",
+ NULL
+ };
+
+ int r = EXIT_FAILURE;
+ pid_t pid;
+
+ if (argc > 1) {
+ log_error("This program takes no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ parse_proc_cmdline();
+ test_files();
+
+ if (!arg_force) {
+ if (arg_skip)
+ return 0;
+
+ if (access("/run/systemd/quotacheck", F_OK) < 0)
+ return 0;
+ }
+
+ if ((pid = fork()) < 0) {
+ log_error("fork(): %m");
+ goto finish;
+ } else if (pid == 0) {
+ /* Child */
+ execv(cmdline[0], (char**) cmdline);
+ _exit(1); /* Operational error */
+ }
+
+ r = wait_for_terminate_and_warn("quotacheck", pid) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+
+finish:
+ return r;
+}
diff --git a/src/random-seed.c b/src/random-seed.c
deleted file mode 100644
index d1cab8b..0000000
--- a/src/random-seed.c
+++ /dev/null
@@ -1,148 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-
-#define POOL_SIZE_MIN 512
-
-int main(int argc, char *argv[]) {
- int seed_fd = -1, random_fd = -1;
- int ret = EXIT_FAILURE;
- void* buf;
- size_t buf_size = 0;
- ssize_t r;
- FILE *f;
-
- if (argc != 2) {
- log_error("This program requires one argument.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- /* Read pool size, if possible */
- if ((f = fopen("/proc/sys/kernel/random/poolsize", "re"))) {
- if (fscanf(f, "%zu", &buf_size) > 0) {
- /* poolsize is in bits on 2.6, but we want bytes */
- buf_size /= 8;
- }
-
- fclose(f);
- }
-
- if (buf_size <= POOL_SIZE_MIN)
- buf_size = POOL_SIZE_MIN;
-
- if (!(buf = malloc(buf_size))) {
- log_error("Failed to allocate buffer.");
- goto finish;
- }
-
- if (mkdir_parents(RANDOM_SEED, 0755) < 0) {
- log_error("Failed to create directories parents of %s: %m", RANDOM_SEED);
- goto finish;
- }
-
- /* When we load the seed we read it and write it to the device
- * and then immediately update the saved seed with new data,
- * to make sure the next boot gets seeded differently. */
-
- if (streq(argv[1], "load")) {
-
- if ((seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
- if ((seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
- log_error("Failed to open random seed: %m");
- goto finish;
- }
- }
-
- if ((random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
- if ((random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
- log_error("Failed to open /dev/urandom: %m");
- goto finish;
- }
- }
-
- if ((r = loop_read(seed_fd, buf, buf_size, false)) <= 0) {
-
- if (r != 0)
- log_error("Failed to read seed file: %m");
- } else {
- lseek(seed_fd, 0, SEEK_SET);
-
- if ((r = loop_write(random_fd, buf, (size_t) r, false)) <= 0)
- log_error("Failed to write seed to /dev/random: %s", r < 0 ? strerror(errno) : "short write");
- }
-
- } else if (streq(argv[1], "save")) {
-
- if ((seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
- log_error("Failed to open random seed: %m");
- goto finish;
- }
-
- if ((random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
- log_error("Failed to open /dev/urandom: %m");
- goto finish;
- }
- } else {
- log_error("Unknown verb %s.", argv[1]);
- goto finish;
- }
-
- /* This is just a safety measure. Given that we are root and
- * most likely created the file ourselves the mode and owner
- * should be correct anyway. */
- fchmod(seed_fd, 0600);
- fchown(seed_fd, 0, 0);
-
- if ((r = loop_read(random_fd, buf, buf_size, false)) <= 0)
- log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(errno) : "EOF");
- else {
- if ((r = loop_write(seed_fd, buf, (size_t) r, false)) <= 0)
- log_error("Failed to write new random seed file: %s", r < 0 ? strerror(errno) : "short write");
- }
-
- ret = EXIT_SUCCESS;
-
-finish:
- if (random_fd >= 0)
- close_nointr_nofail(random_fd);
-
- if (seed_fd >= 0)
- close_nointr_nofail(seed_fd);
-
- free(buf);
-
- return ret;
-}
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
new file mode 100644
index 0000000..d1cab8b
--- /dev/null
+++ b/src/random-seed/random-seed.c
@@ -0,0 +1,148 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+
+#define POOL_SIZE_MIN 512
+
+int main(int argc, char *argv[]) {
+ int seed_fd = -1, random_fd = -1;
+ int ret = EXIT_FAILURE;
+ void* buf;
+ size_t buf_size = 0;
+ ssize_t r;
+ FILE *f;
+
+ if (argc != 2) {
+ log_error("This program requires one argument.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ /* Read pool size, if possible */
+ if ((f = fopen("/proc/sys/kernel/random/poolsize", "re"))) {
+ if (fscanf(f, "%zu", &buf_size) > 0) {
+ /* poolsize is in bits on 2.6, but we want bytes */
+ buf_size /= 8;
+ }
+
+ fclose(f);
+ }
+
+ if (buf_size <= POOL_SIZE_MIN)
+ buf_size = POOL_SIZE_MIN;
+
+ if (!(buf = malloc(buf_size))) {
+ log_error("Failed to allocate buffer.");
+ goto finish;
+ }
+
+ if (mkdir_parents(RANDOM_SEED, 0755) < 0) {
+ log_error("Failed to create directories parents of %s: %m", RANDOM_SEED);
+ goto finish;
+ }
+
+ /* When we load the seed we read it and write it to the device
+ * and then immediately update the saved seed with new data,
+ * to make sure the next boot gets seeded differently. */
+
+ if (streq(argv[1], "load")) {
+
+ if ((seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
+ if ((seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
+ log_error("Failed to open random seed: %m");
+ goto finish;
+ }
+ }
+
+ if ((random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
+ if ((random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
+ log_error("Failed to open /dev/urandom: %m");
+ goto finish;
+ }
+ }
+
+ if ((r = loop_read(seed_fd, buf, buf_size, false)) <= 0) {
+
+ if (r != 0)
+ log_error("Failed to read seed file: %m");
+ } else {
+ lseek(seed_fd, 0, SEEK_SET);
+
+ if ((r = loop_write(random_fd, buf, (size_t) r, false)) <= 0)
+ log_error("Failed to write seed to /dev/random: %s", r < 0 ? strerror(errno) : "short write");
+ }
+
+ } else if (streq(argv[1], "save")) {
+
+ if ((seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
+ log_error("Failed to open random seed: %m");
+ goto finish;
+ }
+
+ if ((random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
+ log_error("Failed to open /dev/urandom: %m");
+ goto finish;
+ }
+ } else {
+ log_error("Unknown verb %s.", argv[1]);
+ goto finish;
+ }
+
+ /* This is just a safety measure. Given that we are root and
+ * most likely created the file ourselves the mode and owner
+ * should be correct anyway. */
+ fchmod(seed_fd, 0600);
+ fchown(seed_fd, 0, 0);
+
+ if ((r = loop_read(random_fd, buf, buf_size, false)) <= 0)
+ log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(errno) : "EOF");
+ else {
+ if ((r = loop_write(seed_fd, buf, (size_t) r, false)) <= 0)
+ log_error("Failed to write new random seed file: %s", r < 0 ? strerror(errno) : "short write");
+ }
+
+ ret = EXIT_SUCCESS;
+
+finish:
+ if (random_fd >= 0)
+ close_nointr_nofail(random_fd);
+
+ if (seed_fd >= 0)
+ close_nointr_nofail(seed_fd);
+
+ free(buf);
+
+ return ret;
+}
diff --git a/src/rc-local-generator.c b/src/rc-local-generator.c
deleted file mode 100644
index 42d7ae4..0000000
--- a/src/rc-local-generator.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
- Copyright 2011 Michal Schmidt
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-#define SCRIPT_PATH "/etc/rc.d/rc.local"
-#elif defined(TARGET_SUSE)
-#define SCRIPT_PATH "/etc/init.d/boot.local"
-#endif
-
-const char *arg_dest = "/tmp";
-
-static int add_symlink(const char *service) {
- char *from = NULL, *to = NULL;
- int r;
-
- assert(service);
-
- asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service);
- asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service);
-
- if (!from || !to) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- mkdir_parents(to, 0755);
-
- r = symlink(from, to);
- if (r < 0) {
- if (errno == EEXIST)
- r = 0;
- else {
- log_error("Failed to create symlink from %s to %s: %m", from, to);
- r = -errno;
- }
- }
-
-finish:
-
- free(from);
- free(to);
-
- return r;
-}
-
-static bool file_is_executable(const char *f) {
- struct stat st;
-
- if (stat(f, &st) < 0)
- return false;
-
- return S_ISREG(st.st_mode) && (st.st_mode & 0111);
-}
-
-int main(int argc, char *argv[]) {
-
- int r = EXIT_SUCCESS;
-
- if (argc > 2) {
- log_error("This program takes one or no arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- if (argc > 1)
- arg_dest = argv[1];
-
- if (file_is_executable(SCRIPT_PATH)) {
- log_debug("Automatically adding rc-local.service.");
-
- if (add_symlink("rc-local.service") < 0)
- r = EXIT_FAILURE;
-
- }
-
- return r;
-}
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
new file mode 100644
index 0000000..42d7ae4
--- /dev/null
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -0,0 +1,108 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+ Copyright 2011 Michal Schmidt
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+#define SCRIPT_PATH "/etc/rc.d/rc.local"
+#elif defined(TARGET_SUSE)
+#define SCRIPT_PATH "/etc/init.d/boot.local"
+#endif
+
+const char *arg_dest = "/tmp";
+
+static int add_symlink(const char *service) {
+ char *from = NULL, *to = NULL;
+ int r;
+
+ assert(service);
+
+ asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service);
+ asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service);
+
+ if (!from || !to) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ mkdir_parents(to, 0755);
+
+ r = symlink(from, to);
+ if (r < 0) {
+ if (errno == EEXIST)
+ r = 0;
+ else {
+ log_error("Failed to create symlink from %s to %s: %m", from, to);
+ r = -errno;
+ }
+ }
+
+finish:
+
+ free(from);
+ free(to);
+
+ return r;
+}
+
+static bool file_is_executable(const char *f) {
+ struct stat st;
+
+ if (stat(f, &st) < 0)
+ return false;
+
+ return S_ISREG(st.st_mode) && (st.st_mode & 0111);
+}
+
+int main(int argc, char *argv[]) {
+
+ int r = EXIT_SUCCESS;
+
+ if (argc > 2) {
+ log_error("This program takes one or no arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ if (argc > 1)
+ arg_dest = argv[1];
+
+ if (file_is_executable(SCRIPT_PATH)) {
+ log_debug("Automatically adding rc-local.service.");
+
+ if (add_symlink("rc-local.service") < 0)
+ r = EXIT_FAILURE;
+
+ }
+
+ return r;
+}
diff --git a/src/remount-api-vfs.c b/src/remount-api-vfs.c
deleted file mode 100644
index 6cb77c1..0000000
--- a/src/remount-api-vfs.c
+++ /dev/null
@@ -1,161 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <mntent.h>
-
-#include "log.h"
-#include "util.h"
-#include "set.h"
-#include "mount-setup.h"
-#include "exit-status.h"
-
-/* Goes through /etc/fstab and remounts all API file systems, applying
- * options that are in /etc/fstab that systemd might not have
- * respected */
-
-int main(int argc, char *argv[]) {
- int ret = EXIT_FAILURE;
- FILE *f = NULL;
- struct mntent* me;
- Hashmap *pids = NULL;
-
- if (argc > 1) {
- log_error("This program takes no argument.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- f = setmntent("/etc/fstab", "r");
- if (!f) {
- log_error("Failed to open /etc/fstab: %m");
- goto finish;
- }
-
- pids = hashmap_new(trivial_hash_func, trivial_compare_func);
- if (!pids) {
- log_error("Failed to allocate set");
- goto finish;
- }
-
- ret = EXIT_SUCCESS;
-
- while ((me = getmntent(f))) {
- pid_t pid;
- int k;
- char *s;
-
- if (!mount_point_is_api(me->mnt_dir))
- continue;
-
- log_debug("Remounting %s", me->mnt_dir);
-
- pid = fork();
- if (pid < 0) {
- log_error("Failed to fork: %m");
- ret = EXIT_FAILURE;
- continue;
- }
-
- if (pid == 0) {
- const char *arguments[5];
- /* Child */
-
- arguments[0] = "/bin/mount";
- arguments[1] = me->mnt_dir;
- arguments[2] = "-o";
- arguments[3] = "remount";
- arguments[4] = NULL;
-
- execv("/bin/mount", (char **) arguments);
-
- log_error("Failed to execute /bin/mount: %m");
- _exit(EXIT_FAILURE);
- }
-
- /* Parent */
-
- s = strdup(me->mnt_dir);
- if (!s) {
- log_error("Out of memory.");
- ret = EXIT_FAILURE;
- continue;
- }
-
-
- k = hashmap_put(pids, UINT_TO_PTR(pid), s);
- if (k < 0) {
- log_error("Failed to add PID to set: %s", strerror(-k));
- ret = EXIT_FAILURE;
- continue;
- }
- }
-
- while (!hashmap_isempty(pids)) {
- siginfo_t si;
- char *s;
-
- zero(si);
- if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- log_error("waitid() failed: %m");
- ret = EXIT_FAILURE;
- break;
- }
-
- s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
- if (s) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
- if (si.si_code == CLD_EXITED)
- log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
- else
- log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status));
-
- ret = EXIT_FAILURE;
- }
-
- free(s);
- }
- }
-
-finish:
-
- if (pids)
- hashmap_free_free(pids);
-
- if (f)
- endmntent(f);
-
- return ret;
-}
diff --git a/src/remount-api-vfs/remount-api-vfs.c b/src/remount-api-vfs/remount-api-vfs.c
new file mode 100644
index 0000000..6cb77c1
--- /dev/null
+++ b/src/remount-api-vfs/remount-api-vfs.c
@@ -0,0 +1,161 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <mntent.h>
+
+#include "log.h"
+#include "util.h"
+#include "set.h"
+#include "mount-setup.h"
+#include "exit-status.h"
+
+/* Goes through /etc/fstab and remounts all API file systems, applying
+ * options that are in /etc/fstab that systemd might not have
+ * respected */
+
+int main(int argc, char *argv[]) {
+ int ret = EXIT_FAILURE;
+ FILE *f = NULL;
+ struct mntent* me;
+ Hashmap *pids = NULL;
+
+ if (argc > 1) {
+ log_error("This program takes no argument.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ f = setmntent("/etc/fstab", "r");
+ if (!f) {
+ log_error("Failed to open /etc/fstab: %m");
+ goto finish;
+ }
+
+ pids = hashmap_new(trivial_hash_func, trivial_compare_func);
+ if (!pids) {
+ log_error("Failed to allocate set");
+ goto finish;
+ }
+
+ ret = EXIT_SUCCESS;
+
+ while ((me = getmntent(f))) {
+ pid_t pid;
+ int k;
+ char *s;
+
+ if (!mount_point_is_api(me->mnt_dir))
+ continue;
+
+ log_debug("Remounting %s", me->mnt_dir);
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ ret = EXIT_FAILURE;
+ continue;
+ }
+
+ if (pid == 0) {
+ const char *arguments[5];
+ /* Child */
+
+ arguments[0] = "/bin/mount";
+ arguments[1] = me->mnt_dir;
+ arguments[2] = "-o";
+ arguments[3] = "remount";
+ arguments[4] = NULL;
+
+ execv("/bin/mount", (char **) arguments);
+
+ log_error("Failed to execute /bin/mount: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Parent */
+
+ s = strdup(me->mnt_dir);
+ if (!s) {
+ log_error("Out of memory.");
+ ret = EXIT_FAILURE;
+ continue;
+ }
+
+
+ k = hashmap_put(pids, UINT_TO_PTR(pid), s);
+ if (k < 0) {
+ log_error("Failed to add PID to set: %s", strerror(-k));
+ ret = EXIT_FAILURE;
+ continue;
+ }
+ }
+
+ while (!hashmap_isempty(pids)) {
+ siginfo_t si;
+ char *s;
+
+ zero(si);
+ if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("waitid() failed: %m");
+ ret = EXIT_FAILURE;
+ break;
+ }
+
+ s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
+ if (s) {
+ if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (si.si_code == CLD_EXITED)
+ log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
+ else
+ log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status));
+
+ ret = EXIT_FAILURE;
+ }
+
+ free(s);
+ }
+ }
+
+finish:
+
+ if (pids)
+ hashmap_free_free(pids);
+
+ if (f)
+ endmntent(f);
+
+ return ret;
+}
diff --git a/src/reply-password.c b/src/reply-password.c
deleted file mode 100644
index a935d0f..0000000
--- a/src/reply-password.c
+++ /dev/null
@@ -1,109 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
-#include <getopt.h>
-#include <stddef.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-
-static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- } sa;
-
- assert(fd >= 0);
- assert(socket_name);
- assert(packet);
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
- if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
- log_error("Failed to send: %m");
- return -1;
- }
-
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- int fd = -1, r = EXIT_FAILURE;
- char packet[LINE_MAX];
- size_t length;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- if (argc != 3) {
- log_error("Wrong number of arguments.");
- goto finish;
- }
-
- if (streq(argv[1], "1")) {
-
- packet[0] = '+';
- if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
- log_error("Failed to read password: %m");
- goto finish;
- }
-
- truncate_nl(packet+1);
- length = 1 + strlen(packet+1) + 1;
- } else if (streq(argv[1], "0")) {
- packet[0] = '-';
- length = 1;
- } else {
- log_error("Invalid first argument %s", argv[1]);
- goto finish;
- }
-
- if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
- log_error("socket() failed: %m");
- goto finish;
- }
-
- if (send_on_socket(fd, argv[2], packet, length) < 0)
- goto finish;
-
- r = EXIT_SUCCESS;
-
-finish:
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- return r;
-}
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
new file mode 100644
index 0000000..a935d0f
--- /dev/null
+++ b/src/reply-password/reply-password.c
@@ -0,0 +1,109 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+
+static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+
+ assert(fd >= 0);
+ assert(socket_name);
+ assert(packet);
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+
+ if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
+ log_error("Failed to send: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ int fd = -1, r = EXIT_FAILURE;
+ char packet[LINE_MAX];
+ size_t length;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ if (argc != 3) {
+ log_error("Wrong number of arguments.");
+ goto finish;
+ }
+
+ if (streq(argv[1], "1")) {
+
+ packet[0] = '+';
+ if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
+ log_error("Failed to read password: %m");
+ goto finish;
+ }
+
+ truncate_nl(packet+1);
+ length = 1 + strlen(packet+1) + 1;
+ } else if (streq(argv[1], "0")) {
+ packet[0] = '-';
+ length = 1;
+ } else {
+ log_error("Invalid first argument %s", argv[1]);
+ goto finish;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ log_error("socket() failed: %m");
+ goto finish;
+ }
+
+ if (send_on_socket(fd, argv[2], packet, length) < 0)
+ goto finish;
+
+ r = EXIT_SUCCESS;
+
+finish:
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
diff --git a/src/shutdownd.c b/src/shutdownd.c
deleted file mode 100644
index 0497cd4..0000000
--- a/src/shutdownd.c
+++ /dev/null
@@ -1,476 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <sys/timerfd.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stddef.h>
-
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-shutdown.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "utmp-wtmp.h"
-#include "mkdir.h"
-
-union shutdown_buffer {
- struct sd_shutdown_command command;
- char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
-};
-
-static int read_packet(int fd, union shutdown_buffer *_b) {
- struct msghdr msghdr;
- struct iovec iovec;
- struct ucred *ucred;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control;
- ssize_t n;
- union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
-
- assert(fd >= 0);
- assert(_b);
-
- zero(iovec);
- iovec.iov_base = &b;
- iovec.iov_len = sizeof(b) - 1;
-
- zero(control);
- zero(msghdr);
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_control = &control;
- msghdr.msg_controllen = sizeof(control);
-
- n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
- if (n <= 0) {
- if (n == 0) {
- log_error("Short read");
- return -EIO;
- }
-
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- log_error("recvmsg(): %m");
- 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 message without credentials. Ignoring.");
- return 0;
- }
-
- ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
- if (ucred->uid != 0) {
- log_warning("Got request from unprivileged user. Ignoring.");
- return 0;
- }
-
- if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
- log_warning("Message has invalid size. Ignoring.");
- return 0;
- }
-
- if (b.command.mode != SD_SHUTDOWN_NONE &&
- b.command.mode != SD_SHUTDOWN_REBOOT &&
- b.command.mode != SD_SHUTDOWN_POWEROFF &&
- b.command.mode != SD_SHUTDOWN_HALT &&
- b.command.mode != SD_SHUTDOWN_KEXEC) {
- log_warning("Message has invalid mode. Ignoring.");
- return 0;
- }
-
- b.space[n] = 0;
-
- *_b = b;
- return 1;
-}
-
-static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
- char date[FORMAT_TIMESTAMP_MAX];
- const char *prefix;
- char *l = NULL;
-
- assert(c);
- assert(c->warn_wall);
-
- if (n >= c->usec)
- return;
-
- if (c->mode == SD_SHUTDOWN_HALT)
- prefix = "The system is going down for system halt at ";
- else if (c->mode == SD_SHUTDOWN_POWEROFF)
- prefix = "The system is going down for power-off at ";
- else if (c->mode == SD_SHUTDOWN_REBOOT)
- prefix = "The system is going down for reboot at ";
- else if (c->mode == SD_SHUTDOWN_KEXEC)
- prefix = "The system is going down for kexec reboot at ";
- else
- assert_not_reached("Unknown mode!");
-
- if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
- prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
- log_error("Failed to allocate wall message");
- else {
- utmp_wall(l, NULL);
- free(l);
- }
-}
-
-static usec_t when_wall(usec_t n, usec_t elapse) {
-
- static const struct {
- usec_t delay;
- usec_t interval;
- } table[] = {
- { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
- { USEC_PER_HOUR, 15 * USEC_PER_MINUTE },
- { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
- };
-
- usec_t left, sub;
- unsigned i;
-
- /* If the time is already passed, then don't announce */
- if (n >= elapse)
- return 0;
-
- left = elapse - n;
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (n + table[i].delay >= elapse) {
- sub = ((left / table[i].interval) * table[i].interval);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
-
- return elapse > sub ? elapse - sub : 1;
-}
-
-static usec_t when_nologin(usec_t elapse) {
- return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
-}
-
-static const char *mode_to_string(enum sd_shutdown_mode m) {
- switch (m) {
- case SD_SHUTDOWN_REBOOT:
- return "reboot";
- case SD_SHUTDOWN_POWEROFF:
- return "poweroff";
- case SD_SHUTDOWN_HALT:
- return "halt";
- case SD_SHUTDOWN_KEXEC:
- return "kexec";
- default:
- return NULL;
- }
-}
-
-static int update_schedule_file(struct sd_shutdown_command *c) {
- int r;
- FILE *f;
- char *temp_path, *t;
-
- assert(c);
-
- r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
- if (r < 0) {
- log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
- return r;
- }
-
- t = cescape(c->wall_message);
- if (!t) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
- if (r < 0) {
- log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
- free(t);
- return r;
- }
-
- fchmod(fileno(f), 0644);
-
- fprintf(f,
- "USEC=%llu\n"
- "WARN_WALL=%i\n"
- "MODE=%s\n",
- (unsigned long long) c->usec,
- c->warn_wall,
- mode_to_string(c->mode));
-
- if (c->dry_run)
- fputs("DRY_RUN=1\n", f);
-
- if (!isempty(t))
- fprintf(f, "WALL_MESSAGE=%s\n", t);
-
- free(t);
-
- fflush(f);
-
- if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
- log_error("Failed to write information about scheduled shutdowns: %m");
- r = -errno;
-
- unlink(temp_path);
- unlink("/run/systemd/shutdown/scheduled");
- }
-
- fclose(f);
- free(temp_path);
-
- return r;
-}
-
-static bool scheduled(struct sd_shutdown_command *c) {
- return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
-}
-
-int main(int argc, char *argv[]) {
- enum {
- FD_SOCKET,
- FD_WALL_TIMER,
- FD_NOLOGIN_TIMER,
- FD_SHUTDOWN_TIMER,
- _FD_MAX
- };
-
- int r = EXIT_FAILURE, n_fds;
- union shutdown_buffer b;
- struct pollfd pollfd[_FD_MAX];
- bool exec_shutdown = false, unlink_nologin = false;
- unsigned i;
-
- if (getppid() != 1) {
- log_error("This program should be invoked by init only.");
- return EXIT_FAILURE;
- }
-
- if (argc > 1) {
- log_error("This program does not take arguments.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- n_fds = sd_listen_fds(true);
- if (n_fds < 0) {
- log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
- return EXIT_FAILURE;
- }
-
- if (n_fds != 1) {
- log_error("Need exactly one file descriptor.");
- return EXIT_FAILURE;
- }
-
- zero(b);
- zero(pollfd);
-
- pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
- pollfd[FD_SOCKET].events = POLLIN;
-
- for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
- pollfd[i].events = POLLIN;
- pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
- if (pollfd[i].fd < 0) {
- log_error("timerfd_create(): %m");
- goto finish;
- }
- }
-
- log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
-
- sd_notify(false,
- "READY=1\n"
- "STATUS=Processing requests...");
-
- for (;;) {
- int k;
- usec_t n;
-
- k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
- if (k < 0) {
-
- if (errno == EAGAIN || errno == EINTR)
- continue;
-
- log_error("poll(): %m");
- goto finish;
- }
-
- /* Exit on idle */
- if (k == 0)
- break;
-
- n = now(CLOCK_REALTIME);
-
- if (pollfd[FD_SOCKET].revents) {
-
- k = read_packet(pollfd[FD_SOCKET].fd, &b);
- if (k < 0)
- goto finish;
- else if (k > 0) {
- struct itimerspec its;
- char date[FORMAT_TIMESTAMP_MAX];
-
- if (!scheduled(&b.command)) {
- log_info("Shutdown canceled.");
- break;
- }
-
- zero(its);
- if (b.command.warn_wall) {
- /* Send wall messages every so often */
- timespec_store(&its.it_value, when_wall(n, b.command.usec));
-
- /* Warn immediately if less than 15 minutes are left */
- if (n < b.command.usec &&
- n + 15*USEC_PER_MINUTE >= b.command.usec)
- warn_wall(n, &b.command);
- }
- if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error("timerfd_settime(): %m");
- goto finish;
- }
-
- /* Disallow logins 5 minutes prior to shutdown */
- zero(its);
- timespec_store(&its.it_value, when_nologin(b.command.usec));
- if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error("timerfd_settime(): %m");
- goto finish;
- }
-
- /* Shutdown after the specified time is reached */
- zero(its);
- timespec_store(&its.it_value, b.command.usec);
- if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error("timerfd_settime(): %m");
- goto finish;
- }
-
- update_schedule_file(&b.command);
-
- sd_notifyf(false,
- "STATUS=Shutting down at %s (%s)...",
- format_timestamp(date, sizeof(date), b.command.usec),
- mode_to_string(b.command.mode));
-
- log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
- }
- }
-
- if (pollfd[FD_WALL_TIMER].revents) {
- struct itimerspec its;
-
- warn_wall(n, &b.command);
- flush_fd(pollfd[FD_WALL_TIMER].fd);
-
- /* Restart timer */
- zero(its);
- timespec_store(&its.it_value, when_wall(n, b.command.usec));
- if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
- log_error("timerfd_settime(): %m");
- goto finish;
- }
- }
-
- if (pollfd[FD_NOLOGIN_TIMER].revents) {
- int e;
-
- log_info("Creating /run/nologin, blocking further logins...");
-
- e = write_one_line_file_atomic("/run/nologin", "System is going down.");
- if (e < 0)
- log_error("Failed to create /run/nologin: %s", strerror(-e));
- else
- unlink_nologin = true;
-
- flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
- }
-
- if (pollfd[FD_SHUTDOWN_TIMER].revents) {
- exec_shutdown = true;
- goto finish;
- }
- }
-
- r = EXIT_SUCCESS;
-
- log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
-
-finish:
-
- for (i = 0; i < _FD_MAX; i++)
- if (pollfd[i].fd >= 0)
- close_nointr_nofail(pollfd[i].fd);
-
- if (unlink_nologin)
- unlink("/run/nologin");
-
- unlink("/run/systemd/shutdown/scheduled");
-
- if (exec_shutdown && !b.command.dry_run) {
- char sw[3];
-
- sw[0] = '-';
- sw[1] = b.command.mode;
- sw[2] = 0;
-
- execl(SYSTEMCTL_BINARY_PATH,
- "shutdown",
- sw,
- "now",
- (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
- (b.command.warn_wall ? NULL : "--no-wall"),
- NULL);
-
- log_error("Failed to execute /sbin/shutdown: %m");
- }
-
- sd_notify(false,
- "STATUS=Exiting...");
-
- return r;
-}
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
new file mode 100644
index 0000000..0497cd4
--- /dev/null
+++ b/src/shutdownd/shutdownd.c
@@ -0,0 +1,476 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/timerfd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "utmp-wtmp.h"
+#include "mkdir.h"
+
+union shutdown_buffer {
+ struct sd_shutdown_command command;
+ char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
+};
+
+static int read_packet(int fd, union shutdown_buffer *_b) {
+ struct msghdr msghdr;
+ struct iovec iovec;
+ struct ucred *ucred;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+ } control;
+ ssize_t n;
+ union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
+
+ assert(fd >= 0);
+ assert(_b);
+
+ zero(iovec);
+ iovec.iov_base = &b;
+ iovec.iov_len = sizeof(b) - 1;
+
+ zero(control);
+ zero(msghdr);
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = &control;
+ msghdr.msg_controllen = sizeof(control);
+
+ n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
+ if (n <= 0) {
+ if (n == 0) {
+ log_error("Short read");
+ return -EIO;
+ }
+
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ log_error("recvmsg(): %m");
+ 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 message without credentials. Ignoring.");
+ return 0;
+ }
+
+ ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
+ if (ucred->uid != 0) {
+ log_warning("Got request from unprivileged user. Ignoring.");
+ return 0;
+ }
+
+ if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
+ log_warning("Message has invalid size. Ignoring.");
+ return 0;
+ }
+
+ if (b.command.mode != SD_SHUTDOWN_NONE &&
+ b.command.mode != SD_SHUTDOWN_REBOOT &&
+ b.command.mode != SD_SHUTDOWN_POWEROFF &&
+ b.command.mode != SD_SHUTDOWN_HALT &&
+ b.command.mode != SD_SHUTDOWN_KEXEC) {
+ log_warning("Message has invalid mode. Ignoring.");
+ return 0;
+ }
+
+ b.space[n] = 0;
+
+ *_b = b;
+ return 1;
+}
+
+static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
+ char date[FORMAT_TIMESTAMP_MAX];
+ const char *prefix;
+ char *l = NULL;
+
+ assert(c);
+ assert(c->warn_wall);
+
+ if (n >= c->usec)
+ return;
+
+ if (c->mode == SD_SHUTDOWN_HALT)
+ prefix = "The system is going down for system halt at ";
+ else if (c->mode == SD_SHUTDOWN_POWEROFF)
+ prefix = "The system is going down for power-off at ";
+ else if (c->mode == SD_SHUTDOWN_REBOOT)
+ prefix = "The system is going down for reboot at ";
+ else if (c->mode == SD_SHUTDOWN_KEXEC)
+ prefix = "The system is going down for kexec reboot at ";
+ else
+ assert_not_reached("Unknown mode!");
+
+ if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
+ prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
+ log_error("Failed to allocate wall message");
+ else {
+ utmp_wall(l, NULL);
+ free(l);
+ }
+}
+
+static usec_t when_wall(usec_t n, usec_t elapse) {
+
+ static const struct {
+ usec_t delay;
+ usec_t interval;
+ } table[] = {
+ { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
+ { USEC_PER_HOUR, 15 * USEC_PER_MINUTE },
+ { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
+ };
+
+ usec_t left, sub;
+ unsigned i;
+
+ /* If the time is already passed, then don't announce */
+ if (n >= elapse)
+ return 0;
+
+ left = elapse - n;
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (n + table[i].delay >= elapse) {
+ sub = ((left / table[i].interval) * table[i].interval);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
+
+ return elapse > sub ? elapse - sub : 1;
+}
+
+static usec_t when_nologin(usec_t elapse) {
+ return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
+}
+
+static const char *mode_to_string(enum sd_shutdown_mode m) {
+ switch (m) {
+ case SD_SHUTDOWN_REBOOT:
+ return "reboot";
+ case SD_SHUTDOWN_POWEROFF:
+ return "poweroff";
+ case SD_SHUTDOWN_HALT:
+ return "halt";
+ case SD_SHUTDOWN_KEXEC:
+ return "kexec";
+ default:
+ return NULL;
+ }
+}
+
+static int update_schedule_file(struct sd_shutdown_command *c) {
+ int r;
+ FILE *f;
+ char *temp_path, *t;
+
+ assert(c);
+
+ r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
+ if (r < 0) {
+ log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
+ return r;
+ }
+
+ t = cescape(c->wall_message);
+ if (!t) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+ if (r < 0) {
+ log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
+ free(t);
+ return r;
+ }
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "USEC=%llu\n"
+ "WARN_WALL=%i\n"
+ "MODE=%s\n",
+ (unsigned long long) c->usec,
+ c->warn_wall,
+ mode_to_string(c->mode));
+
+ if (c->dry_run)
+ fputs("DRY_RUN=1\n", f);
+
+ if (!isempty(t))
+ fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+ free(t);
+
+ fflush(f);
+
+ if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+ log_error("Failed to write information about scheduled shutdowns: %m");
+ r = -errno;
+
+ unlink(temp_path);
+ unlink("/run/systemd/shutdown/scheduled");
+ }
+
+ fclose(f);
+ free(temp_path);
+
+ return r;
+}
+
+static bool scheduled(struct sd_shutdown_command *c) {
+ return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
+}
+
+int main(int argc, char *argv[]) {
+ enum {
+ FD_SOCKET,
+ FD_WALL_TIMER,
+ FD_NOLOGIN_TIMER,
+ FD_SHUTDOWN_TIMER,
+ _FD_MAX
+ };
+
+ int r = EXIT_FAILURE, n_fds;
+ union shutdown_buffer b;
+ struct pollfd pollfd[_FD_MAX];
+ bool exec_shutdown = false, unlink_nologin = false;
+ unsigned i;
+
+ if (getppid() != 1) {
+ log_error("This program should be invoked by init only.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1) {
+ log_error("This program does not take arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ n_fds = sd_listen_fds(true);
+ if (n_fds < 0) {
+ log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ if (n_fds != 1) {
+ log_error("Need exactly one file descriptor.");
+ return EXIT_FAILURE;
+ }
+
+ zero(b);
+ zero(pollfd);
+
+ pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
+ pollfd[FD_SOCKET].events = POLLIN;
+
+ for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
+ pollfd[i].events = POLLIN;
+ pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (pollfd[i].fd < 0) {
+ log_error("timerfd_create(): %m");
+ goto finish;
+ }
+ }
+
+ log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing requests...");
+
+ for (;;) {
+ int k;
+ usec_t n;
+
+ k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
+ if (k < 0) {
+
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+
+ log_error("poll(): %m");
+ goto finish;
+ }
+
+ /* Exit on idle */
+ if (k == 0)
+ break;
+
+ n = now(CLOCK_REALTIME);
+
+ if (pollfd[FD_SOCKET].revents) {
+
+ k = read_packet(pollfd[FD_SOCKET].fd, &b);
+ if (k < 0)
+ goto finish;
+ else if (k > 0) {
+ struct itimerspec its;
+ char date[FORMAT_TIMESTAMP_MAX];
+
+ if (!scheduled(&b.command)) {
+ log_info("Shutdown canceled.");
+ break;
+ }
+
+ zero(its);
+ if (b.command.warn_wall) {
+ /* Send wall messages every so often */
+ timespec_store(&its.it_value, when_wall(n, b.command.usec));
+
+ /* Warn immediately if less than 15 minutes are left */
+ if (n < b.command.usec &&
+ n + 15*USEC_PER_MINUTE >= b.command.usec)
+ warn_wall(n, &b.command);
+ }
+ if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+ log_error("timerfd_settime(): %m");
+ goto finish;
+ }
+
+ /* Disallow logins 5 minutes prior to shutdown */
+ zero(its);
+ timespec_store(&its.it_value, when_nologin(b.command.usec));
+ if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+ log_error("timerfd_settime(): %m");
+ goto finish;
+ }
+
+ /* Shutdown after the specified time is reached */
+ zero(its);
+ timespec_store(&its.it_value, b.command.usec);
+ if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+ log_error("timerfd_settime(): %m");
+ goto finish;
+ }
+
+ update_schedule_file(&b.command);
+
+ sd_notifyf(false,
+ "STATUS=Shutting down at %s (%s)...",
+ format_timestamp(date, sizeof(date), b.command.usec),
+ mode_to_string(b.command.mode));
+
+ log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
+ }
+ }
+
+ if (pollfd[FD_WALL_TIMER].revents) {
+ struct itimerspec its;
+
+ warn_wall(n, &b.command);
+ flush_fd(pollfd[FD_WALL_TIMER].fd);
+
+ /* Restart timer */
+ zero(its);
+ timespec_store(&its.it_value, when_wall(n, b.command.usec));
+ if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+ log_error("timerfd_settime(): %m");
+ goto finish;
+ }
+ }
+
+ if (pollfd[FD_NOLOGIN_TIMER].revents) {
+ int e;
+
+ log_info("Creating /run/nologin, blocking further logins...");
+
+ e = write_one_line_file_atomic("/run/nologin", "System is going down.");
+ if (e < 0)
+ log_error("Failed to create /run/nologin: %s", strerror(-e));
+ else
+ unlink_nologin = true;
+
+ flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
+ }
+
+ if (pollfd[FD_SHUTDOWN_TIMER].revents) {
+ exec_shutdown = true;
+ goto finish;
+ }
+ }
+
+ r = EXIT_SUCCESS;
+
+ log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
+
+finish:
+
+ for (i = 0; i < _FD_MAX; i++)
+ if (pollfd[i].fd >= 0)
+ close_nointr_nofail(pollfd[i].fd);
+
+ if (unlink_nologin)
+ unlink("/run/nologin");
+
+ unlink("/run/systemd/shutdown/scheduled");
+
+ if (exec_shutdown && !b.command.dry_run) {
+ char sw[3];
+
+ sw[0] = '-';
+ sw[1] = b.command.mode;
+ sw[2] = 0;
+
+ execl(SYSTEMCTL_BINARY_PATH,
+ "shutdown",
+ sw,
+ "now",
+ (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
+ (b.command.warn_wall ? NULL : "--no-wall"),
+ NULL);
+
+ log_error("Failed to execute /sbin/shutdown: %m");
+ }
+
+ sd_notify(false,
+ "STATUS=Exiting...");
+
+ return r;
+}
diff --git a/src/sysctl.c b/src/sysctl.c
deleted file mode 100644
index 57fba25..0000000
--- a/src/sysctl.c
+++ /dev/null
@@ -1,272 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <getopt.h>
-
-#include "log.h"
-#include "strv.h"
-#include "util.h"
-#include "strv.h"
-
-#define PROC_SYS_PREFIX "/proc/sys/"
-
-static char **arg_prefixes = NULL;
-
-static int apply_sysctl(const char *property, const char *value) {
- char *p, *n;
- int r = 0, k;
-
- log_debug("Setting '%s' to '%s'", property, value);
-
- p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
- if (!p) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- n = stpcpy(p, PROC_SYS_PREFIX);
- strcpy(n, property);
-
- for (; *n; n++)
- if (*n == '.')
- *n = '/';
-
- if (!strv_isempty(arg_prefixes)) {
- char **i;
- bool good = false;
-
- STRV_FOREACH(i, arg_prefixes)
- if (path_startswith(p, *i)) {
- good = true;
- break;
- }
-
- if (!good) {
- log_debug("Skipping %s", p);
- free(p);
- return 0;
- }
- }
-
- k = write_one_line_file(p, value);
- if (k < 0) {
-
- log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
- "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
-
- if (k != -ENOENT && r == 0)
- r = k;
- }
-
- free(p);
-
- return r;
-}
-
-static int apply_file(const char *path, bool ignore_enoent) {
- FILE *f;
- int r = 0;
-
- assert(path);
-
- if (!(f = fopen(path, "re"))) {
- if (ignore_enoent && errno == ENOENT)
- return 0;
-
- log_error("Failed to open file '%s', ignoring: %m", path);
- return -errno;
- }
-
- log_debug("apply: %s\n", path);
- while (!feof(f)) {
- char l[LINE_MAX], *p, *value;
- int k;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- log_error("Failed to read file '%s', ignoring: %m", path);
- r = -errno;
- goto finish;
- }
-
- p = strstrip(l);
-
- if (!*p)
- continue;
-
- if (strchr(COMMENTS, *p))
- continue;
-
- if (!(value = strchr(p, '='))) {
- log_error("Line is not an assignment in file '%s': %s", path, value);
-
- if (r == 0)
- r = -EINVAL;
- continue;
- }
-
- *value = 0;
- value++;
-
- if ((k = apply_sysctl(strstrip(p), strstrip(value))) < 0 && r == 0)
- r = k;
- }
-
-finish:
- fclose(f);
-
- return r;
-}
-
-static int help(void) {
-
- printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
- "Applies kernel sysctl settings.\n\n"
- " -h --help Show this help\n"
- " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_PREFIX
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "prefix", required_argument, NULL, ARG_PREFIX },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_PREFIX: {
- char *p;
- char **l;
-
- for (p = optarg; *p; p++)
- if (*p == '.')
- *p = '/';
-
- l = strv_append(arg_prefixes, optarg);
- if (!l) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- strv_free(arg_prefixes);
- arg_prefixes = l;
-
- break;
- }
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r = 0;
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if (argc > optind) {
- int i;
-
- for (i = optind; i < argc; i++) {
- int k;
-
- k = apply_file(argv[i], false);
- if (k < 0 && r == 0)
- r = k;
- }
- } else {
- char **files, **f;
- int k;
-
- r = conf_files_list(&files, ".conf",
- "/etc/sysctl.d",
- "/run/sysctl.d",
- "/usr/local/lib/sysctl.d",
- "/usr/lib/sysctl.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/sysctl.d",
-#endif
- NULL);
- if (r < 0) {
- log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
- goto finish;
- }
-
- STRV_FOREACH(f, files) {
- k = apply_file(*f, true);
- if (k < 0 && r == 0)
- r = k;
- }
-
- k = apply_file("/etc/sysctl.conf", true);
- if (k < 0 && r == 0)
- r = k;
-
- strv_free(files);
- }
-finish:
- strv_free(arg_prefixes);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
new file mode 100644
index 0000000..57fba25
--- /dev/null
+++ b/src/sysctl/sysctl.c
@@ -0,0 +1,272 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "log.h"
+#include "strv.h"
+#include "util.h"
+#include "strv.h"
+
+#define PROC_SYS_PREFIX "/proc/sys/"
+
+static char **arg_prefixes = NULL;
+
+static int apply_sysctl(const char *property, const char *value) {
+ char *p, *n;
+ int r = 0, k;
+
+ log_debug("Setting '%s' to '%s'", property, value);
+
+ p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
+ if (!p) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ n = stpcpy(p, PROC_SYS_PREFIX);
+ strcpy(n, property);
+
+ for (; *n; n++)
+ if (*n == '.')
+ *n = '/';
+
+ if (!strv_isempty(arg_prefixes)) {
+ char **i;
+ bool good = false;
+
+ STRV_FOREACH(i, arg_prefixes)
+ if (path_startswith(p, *i)) {
+ good = true;
+ break;
+ }
+
+ if (!good) {
+ log_debug("Skipping %s", p);
+ free(p);
+ return 0;
+ }
+ }
+
+ k = write_one_line_file(p, value);
+ if (k < 0) {
+
+ log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
+ "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
+
+ if (k != -ENOENT && r == 0)
+ r = k;
+ }
+
+ free(p);
+
+ return r;
+}
+
+static int apply_file(const char *path, bool ignore_enoent) {
+ FILE *f;
+ int r = 0;
+
+ assert(path);
+
+ if (!(f = fopen(path, "re"))) {
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open file '%s', ignoring: %m", path);
+ return -errno;
+ }
+
+ log_debug("apply: %s\n", path);
+ while (!feof(f)) {
+ char l[LINE_MAX], *p, *value;
+ int k;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ log_error("Failed to read file '%s', ignoring: %m", path);
+ r = -errno;
+ goto finish;
+ }
+
+ p = strstrip(l);
+
+ if (!*p)
+ continue;
+
+ if (strchr(COMMENTS, *p))
+ continue;
+
+ if (!(value = strchr(p, '='))) {
+ log_error("Line is not an assignment in file '%s': %s", path, value);
+
+ if (r == 0)
+ r = -EINVAL;
+ continue;
+ }
+
+ *value = 0;
+ value++;
+
+ if ((k = apply_sysctl(strstrip(p), strstrip(value))) < 0 && r == 0)
+ r = k;
+ }
+
+finish:
+ fclose(f);
+
+ return r;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+ "Applies kernel sysctl settings.\n\n"
+ " -h --help Show this help\n"
+ " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_PREFIX
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "prefix", required_argument, NULL, ARG_PREFIX },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_PREFIX: {
+ char *p;
+ char **l;
+
+ for (p = optarg; *p; p++)
+ if (*p == '.')
+ *p = '/';
+
+ l = strv_append(arg_prefixes, optarg);
+ if (!l) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ strv_free(arg_prefixes);
+ arg_prefixes = l;
+
+ break;
+ }
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r = 0;
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if (argc > optind) {
+ int i;
+
+ for (i = optind; i < argc; i++) {
+ int k;
+
+ k = apply_file(argv[i], false);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+ } else {
+ char **files, **f;
+ int k;
+
+ r = conf_files_list(&files, ".conf",
+ "/etc/sysctl.d",
+ "/run/sysctl.d",
+ "/usr/local/lib/sysctl.d",
+ "/usr/lib/sysctl.d",
+#ifdef HAVE_SPLIT_USR
+ "/lib/sysctl.d",
+#endif
+ NULL);
+ if (r < 0) {
+ log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
+ goto finish;
+ }
+
+ STRV_FOREACH(f, files) {
+ k = apply_file(*f, true);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ k = apply_file("/etc/sysctl.conf", true);
+ if (k < 0 && r == 0)
+ r = k;
+
+ strv_free(files);
+ }
+finish:
+ strv_free(arg_prefixes);
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/systemctl.c b/src/systemctl.c
deleted file mode 100644
index 28bdfa9..0000000
--- a/src/systemctl.c
+++ /dev/null
@@ -1,5523 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/reboot.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <sys/prctl.h>
-#include <dbus/dbus.h>
-
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-shutdown.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "set.h"
-#include "utmp-wtmp.h"
-#include "special.h"
-#include "initreq.h"
-#include "strv.h"
-#include "dbus-common.h"
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "list.h"
-#include "path-lookup.h"
-#include "conf-parser.h"
-#include "exit-status.h"
-#include "bus-errors.h"
-#include "build.h"
-#include "unit-name.h"
-#include "pager.h"
-#include "spawn-ask-password-agent.h"
-#include "spawn-polkit-agent.h"
-#include "install.h"
-#include "logs-show.h"
-
-static const char *arg_type = NULL;
-static char **arg_property = NULL;
-static bool arg_all = false;
-static const char *arg_job_mode = "replace";
-static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
-static bool arg_immediate = false;
-static bool arg_no_block = false;
-static bool arg_no_legend = false;
-static bool arg_no_pager = false;
-static bool arg_no_wtmp = false;
-static bool arg_no_sync = false;
-static bool arg_no_wall = false;
-static bool arg_no_reload = false;
-static bool arg_dry = false;
-static bool arg_quiet = false;
-static bool arg_full = false;
-static int arg_force = 0;
-static bool arg_ask_password = true;
-static bool arg_failed = false;
-static bool arg_runtime = false;
-static char **arg_wall = NULL;
-static const char *arg_kill_who = NULL;
-static const char *arg_kill_mode = NULL;
-static int arg_signal = SIGTERM;
-static const char *arg_root = NULL;
-static usec_t arg_when = 0;
-static enum action {
- ACTION_INVALID,
- ACTION_SYSTEMCTL,
- ACTION_HALT,
- ACTION_POWEROFF,
- ACTION_REBOOT,
- ACTION_KEXEC,
- ACTION_EXIT,
- ACTION_RUNLEVEL2,
- ACTION_RUNLEVEL3,
- ACTION_RUNLEVEL4,
- ACTION_RUNLEVEL5,
- ACTION_RESCUE,
- ACTION_EMERGENCY,
- ACTION_DEFAULT,
- ACTION_RELOAD,
- ACTION_REEXEC,
- ACTION_RUNLEVEL,
- ACTION_CANCEL_SHUTDOWN,
- _ACTION_MAX
-} arg_action = ACTION_SYSTEMCTL;
-static enum dot {
- DOT_ALL,
- DOT_ORDER,
- DOT_REQUIRE
-} arg_dot = DOT_ALL;
-static enum transport {
- TRANSPORT_NORMAL,
- TRANSPORT_SSH,
- TRANSPORT_POLKIT
-} arg_transport = TRANSPORT_NORMAL;
-static const char *arg_host = NULL;
-static bool arg_follow = false;
-static unsigned arg_lines = 10;
-static OutputMode arg_output = OUTPUT_SHORT;
-
-static bool private_bus = false;
-
-static int daemon_reload(DBusConnection *bus, char **args);
-static void halt_now(enum action a);
-
-static bool on_tty(void) {
- static int t = -1;
-
- /* Note that this is invoked relatively early, before we start
- * the pager. That means the value we return reflects whether
- * we originally were started on a tty, not if we currently
- * are. But this is intended, since we want colour and so on
- * when run in our own pager. */
-
- if (_unlikely_(t < 0))
- t = isatty(STDOUT_FILENO) > 0;
-
- return t;
-}
-
-static void pager_open_if_enabled(void) {
-
- /* Cache result before we open the pager */
- on_tty();
-
- if (arg_no_pager)
- return;
-
- pager_open();
-}
-
-static void ask_password_agent_open_if_enabled(void) {
-
- /* Open the password agent as a child process if necessary */
-
- if (!arg_ask_password)
- return;
-
- if (arg_scope != UNIT_FILE_SYSTEM)
- return;
-
- ask_password_agent_open();
-}
-
-static void polkit_agent_open_if_enabled(void) {
-
- /* Open the polkit agent as a child process if necessary */
-
- if (!arg_ask_password)
- return;
-
- if (arg_scope != UNIT_FILE_SYSTEM)
- return;
-
- polkit_agent_open();
-}
-
-static const char *ansi_highlight_red(bool b) {
-
- if (!on_tty())
- return "";
-
- return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
-}
-
-static const char *ansi_highlight_green(bool b) {
-
- if (!on_tty())
- return "";
-
- return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
-}
-
-static bool error_is_no_service(const DBusError *error) {
- assert(error);
-
- if (!dbus_error_is_set(error))
- return false;
-
- if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
- return true;
-
- if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
- return true;
-
- return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
-}
-
-static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
- assert(error);
-
- if (!dbus_error_is_set(error))
- return r;
-
- if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
- dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
- dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
- dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
- return EXIT_NOPERMISSION;
-
- if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
- return EXIT_NOTINSTALLED;
-
- if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
- dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
- return EXIT_NOTIMPLEMENTED;
-
- if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
- return EXIT_NOTCONFIGURED;
-
- if (r != 0)
- return r;
-
- return EXIT_FAILURE;
-}
-
-static void warn_wall(enum action a) {
- static const char *table[_ACTION_MAX] = {
- [ACTION_HALT] = "The system is going down for system halt NOW!",
- [ACTION_REBOOT] = "The system is going down for reboot NOW!",
- [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
- [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!",
- [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
- [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
- };
-
- if (arg_no_wall)
- return;
-
- if (arg_wall) {
- char *p;
-
- p = strv_join(arg_wall, " ");
- if (!p) {
- log_error("Failed to join strings.");
- return;
- }
-
- if (*p) {
- utmp_wall(p, NULL);
- free(p);
- return;
- }
-
- free(p);
- }
-
- if (!table[a])
- return;
-
- utmp_wall(table[a], NULL);
-}
-
-static bool avoid_bus(void) {
-
- if (running_in_chroot() > 0)
- return true;
-
- if (sd_booted() <= 0)
- return true;
-
- if (!isempty(arg_root))
- return true;
-
- if (arg_scope == UNIT_FILE_GLOBAL)
- return true;
-
- return false;
-}
-
-struct unit_info {
- const char *id;
- const char *description;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *following;
- const char *unit_path;
- uint32_t job_id;
- const char *job_type;
- const char *job_path;
-};
-
-static int compare_unit_info(const void *a, const void *b) {
- const char *d1, *d2;
- const struct unit_info *u = a, *v = b;
-
- d1 = strrchr(u->id, '.');
- d2 = strrchr(v->id, '.');
-
- if (d1 && d2) {
- int r;
-
- if ((r = strcasecmp(d1, d2)) != 0)
- return r;
- }
-
- return strcasecmp(u->id, v->id);
-}
-
-static bool output_show_unit(const struct unit_info *u) {
- const char *dot;
-
- if (arg_failed)
- return streq(u->active_state, "failed");
-
- return (!arg_type || ((dot = strrchr(u->id, '.')) &&
- streq(dot+1, arg_type))) &&
- (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
-}
-
-static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
- unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
- const struct unit_info *u;
-
- max_id_len = sizeof("UNIT")-1;
- active_len = sizeof("ACTIVE")-1;
- sub_len = sizeof("SUB")-1;
- job_len = sizeof("JOB")-1;
- desc_len = 0;
-
- for (u = unit_infos; u < unit_infos + c; u++) {
- if (!output_show_unit(u))
- continue;
-
- max_id_len = MAX(max_id_len, strlen(u->id));
- active_len = MAX(active_len, strlen(u->active_state));
- sub_len = MAX(sub_len, strlen(u->sub_state));
- if (u->job_id != 0)
- job_len = MAX(job_len, strlen(u->job_type));
- }
-
- if (!arg_full) {
- unsigned basic_len;
- id_len = MIN(max_id_len, 25);
- basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
- if (basic_len < (unsigned) columns()) {
- unsigned extra_len, incr;
- extra_len = columns() - basic_len;
- /* Either UNIT already got 25, or is fully satisfied.
- * Grant up to 25 to DESC now. */
- incr = MIN(extra_len, 25);
- desc_len += incr;
- extra_len -= incr;
- /* split the remaining space between UNIT and DESC,
- * but do not give UNIT more than it needs. */
- if (extra_len > 0) {
- incr = MIN(extra_len / 2, max_id_len - id_len);
- id_len += incr;
- desc_len += extra_len - incr;
- }
- }
- } else
- id_len = max_id_len;
-
- if (!arg_no_legend) {
- printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
- active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
- if (!arg_full && arg_no_pager)
- printf("%.*s\n", desc_len, "DESCRIPTION");
- else
- printf("%s\n", "DESCRIPTION");
- }
-
- for (u = unit_infos; u < unit_infos + c; u++) {
- char *e;
- const char *on_loaded, *off_loaded;
- const char *on_active, *off_active;
-
- if (!output_show_unit(u))
- continue;
-
- n_shown++;
-
- if (streq(u->load_state, "error")) {
- on_loaded = ansi_highlight_red(true);
- off_loaded = ansi_highlight_red(false);
- } else
- on_loaded = off_loaded = "";
-
- if (streq(u->active_state, "failed")) {
- on_active = ansi_highlight_red(true);
- off_active = ansi_highlight_red(false);
- } else
- on_active = off_active = "";
-
- e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
-
- printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
- id_len, e ? e : u->id,
- on_loaded, u->load_state, off_loaded,
- on_active, active_len, u->active_state,
- sub_len, u->sub_state, off_active,
- job_len, u->job_id ? u->job_type : "");
- if (!arg_full && arg_no_pager)
- printf("%.*s\n", desc_len, u->description);
- else
- printf("%s\n", u->description);
-
- free(e);
- }
-
- if (!arg_no_legend) {
- printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n"
- "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
- "SUB = The low-level unit activation state, values depend on unit type.\n"
- "JOB = Pending job for the unit.\n");
-
- if (arg_all)
- printf("\n%u units listed.\n", n_shown);
- else
- printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
- }
-}
-
-static int list_units(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- DBusMessageIter iter, sub, sub2;
- unsigned c = 0, n_units = 0;
- struct unit_info *unit_infos = NULL;
-
- dbus_error_init(&error);
-
- assert(bus);
-
- pager_open_if_enabled();
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnits"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- struct unit_info *u;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (c >= n_units) {
- struct unit_info *w;
-
- n_units = MAX(2*c, 16);
- w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
-
- if (!w) {
- log_error("Failed to allocate unit array.");
- r = -ENOMEM;
- goto finish;
- }
-
- unit_infos = w;
- }
-
- u = unit_infos+c;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_next(&sub);
- c++;
- }
-
- if (c > 0) {
- qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
- output_units_list(unit_infos, c);
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- free(unit_infos);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int compare_unit_file_list(const void *a, const void *b) {
- const char *d1, *d2;
- const UnitFileList *u = a, *v = b;
-
- d1 = strrchr(u->path, '.');
- d2 = strrchr(v->path, '.');
-
- if (d1 && d2) {
- int r;
-
- r = strcasecmp(d1, d2);
- if (r != 0)
- return r;
- }
-
- return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
-}
-
-static bool output_show_unit_file(const UnitFileList *u) {
- const char *dot;
-
- return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
-}
-
-static void output_unit_file_list(const UnitFileList *units, unsigned c) {
- unsigned max_id_len, id_cols, state_cols, n_shown = 0;
- const UnitFileList *u;
-
- max_id_len = sizeof("UNIT FILE")-1;
- state_cols = sizeof("STATE")-1;
- for (u = units; u < units + c; u++) {
- if (!output_show_unit_file(u))
- continue;
-
- max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
- state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
- }
-
- if (!arg_full) {
- unsigned basic_cols;
- id_cols = MIN(max_id_len, 25);
- basic_cols = 1 + id_cols + state_cols;
- if (basic_cols < (unsigned) columns())
- id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
- } else
- id_cols = max_id_len;
-
- if (!arg_no_legend)
- printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
-
- for (u = units; u < units + c; u++) {
- char *e;
- const char *on, *off;
- const char *id;
-
- if (!output_show_unit_file(u))
- continue;
-
- n_shown++;
-
- if (u->state == UNIT_FILE_MASKED ||
- u->state == UNIT_FILE_MASKED_RUNTIME ||
- u->state == UNIT_FILE_DISABLED) {
- on = ansi_highlight_red(true);
- off = ansi_highlight_red(false);
- } else if (u->state == UNIT_FILE_ENABLED) {
- on = ansi_highlight_green(true);
- off = ansi_highlight_green(false);
- } else
- on = off = "";
-
- id = file_name_from_path(u->path);
-
- e = arg_full ? NULL : ellipsize(id, id_cols, 33);
-
- printf("%-*s %s%-*s%s\n",
- id_cols, e ? e : id,
- on, state_cols, unit_file_state_to_string(u->state), off);
-
- free(e);
- }
-
- if (!arg_no_legend)
- printf("\n%u unit files listed.\n", n_shown);
-}
-
-static int list_unit_files(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- DBusMessageIter iter, sub, sub2;
- unsigned c = 0, n_units = 0;
- UnitFileList *units = NULL;
-
- dbus_error_init(&error);
-
- pager_open_if_enabled();
-
- if (avoid_bus()) {
- Hashmap *h;
- UnitFileList *u;
- Iterator i;
-
- h = hashmap_new(string_hash_func, string_compare_func);
- if (!h) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- r = unit_file_get_list(arg_scope, arg_root, h);
- if (r < 0) {
- unit_file_list_free(h);
- log_error("Failed to get unit file list: %s", strerror(-r));
- return r;
- }
-
- n_units = hashmap_size(h);
- units = new(UnitFileList, n_units);
- if (!units) {
- unit_file_list_free(h);
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- HASHMAP_FOREACH(u, h, i) {
- memcpy(units + c++, u, sizeof(UnitFileList));
- free(u);
- }
-
- hashmap_free(h);
- } else {
- assert(bus);
-
- m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnitFiles");
- if (!m) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- UnitFileList *u;
- const char *state;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (c >= n_units) {
- UnitFileList *w;
-
- n_units = MAX(2*c, 16);
- w = realloc(units, sizeof(struct UnitFileList) * n_units);
-
- if (!w) {
- log_error("Failed to allocate unit array.");
- r = -ENOMEM;
- goto finish;
- }
-
- units = w;
- }
-
- u = units+c;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- u->state = unit_file_state_from_string(state);
-
- dbus_message_iter_next(&sub);
- c++;
- }
- }
-
- if (c > 0) {
- qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
- output_unit_file_list(units, c);
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- free(units);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
- static const char * const colors[] = {
- "Requires", "[color=\"black\"]",
- "RequiresOverridable", "[color=\"black\"]",
- "Requisite", "[color=\"darkblue\"]",
- "RequisiteOverridable", "[color=\"darkblue\"]",
- "Wants", "[color=\"darkgrey\"]",
- "Conflicts", "[color=\"red\"]",
- "ConflictedBy", "[color=\"red\"]",
- "After", "[color=\"green\"]"
- };
-
- const char *c = NULL;
- unsigned i;
-
- assert(name);
- assert(prop);
- assert(iter);
-
- for (i = 0; i < ELEMENTSOF(colors); i += 2)
- if (streq(colors[i], prop)) {
- c = colors[i+1];
- break;
- }
-
- if (!c)
- return 0;
-
- if (arg_dot != DOT_ALL)
- if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
- return 0;
-
- switch (dbus_message_iter_get_arg_type(iter)) {
-
- case DBUS_TYPE_ARRAY:
-
- if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
- DBusMessageIter sub;
-
- dbus_message_iter_recurse(iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *s;
-
- assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
- dbus_message_iter_get_basic(&sub, &s);
- printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
- }
- }
-
- return 0;
-}
-
-static int dot_one(DBusConnection *bus, const char *name, const char *path) {
- DBusMessage *m = NULL, *reply = NULL;
- const char *interface = "org.freedesktop.systemd1.Unit";
- int r;
- DBusError error;
- DBusMessageIter iter, sub, sub2, sub3;
-
- assert(bus);
- assert(path);
-
- dbus_error_init(&error);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *prop;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub2, &sub3);
-
- if (dot_one_property(name, prop, &sub3)) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_next(&sub);
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int dot(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- DBusMessageIter iter, sub, sub2;
-
- dbus_error_init(&error);
-
- assert(bus);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnits"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- printf("digraph systemd {\n");
-
- dbus_message_iter_recurse(&iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if ((r = dot_one(bus, id, unit_path)) < 0)
- goto finish;
-
- /* printf("\t\"%s\";\n", id); */
- dbus_message_iter_next(&sub);
- }
-
- printf("}\n");
-
- log_info(" Color legend: black = Requires\n"
- " dark blue = Requisite\n"
- " dark grey = Wants\n"
- " red = Conflicts\n"
- " green = After\n");
-
- if (on_tty())
- log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
- "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int list_jobs(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- DBusMessageIter iter, sub, sub2;
- unsigned k = 0;
-
- dbus_error_init(&error);
-
- assert(bus);
-
- pager_open_if_enabled();
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListJobs"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (on_tty())
- printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *name, *type, *state, *job_path, *unit_path;
- uint32_t id;
- char *e;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- e = arg_full ? NULL : ellipsize(name, 25, 33);
- printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
- free(e);
-
- k++;
-
- dbus_message_iter_next(&sub);
- }
-
- if (on_tty())
- printf("\n%u jobs listed.\n", k);
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int load_unit(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL;
- DBusError error;
- int r;
- char **name;
-
- dbus_error_init(&error);
-
- assert(bus);
- assert(args);
-
- STRV_FOREACH(name, args+1) {
- DBusMessage *reply;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LoadUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
-
- m = reply = NULL;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int cancel_job(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- char **name;
-
- dbus_error_init(&error);
-
- assert(bus);
- assert(args);
-
- if (strv_length(args) <= 1)
- return daemon_reload(bus, args);
-
- STRV_FOREACH(name, args+1) {
- unsigned id;
- const char *path;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetJob"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = safe_atou(*name, &id)) < 0) {
- log_error("Failed to parse job id: %s", strerror(-r));
- goto finish;
- }
-
- assert_cc(sizeof(uint32_t) == sizeof(id));
- if (!dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Job",
- "Cancel"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
- DBusMessage *m = NULL, *reply = NULL;
- dbus_bool_t b = FALSE;
- DBusMessageIter iter, sub;
- const char
- *interface = "org.freedesktop.systemd1.Unit",
- *property = "NeedDaemonReload",
- *path;
-
- /* We ignore all errors here, since this is used to show a warning only */
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit")))
- goto finish;
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &unit,
- DBUS_TYPE_INVALID))
- goto finish;
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
- goto finish;
-
- if (!dbus_message_get_args(reply, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto finish;
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "Get")))
- goto finish;
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
- goto finish;
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
- goto finish;
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
- goto finish;
-
- dbus_message_iter_get_basic(&sub, &b);
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- return b;
-}
-
-typedef struct WaitData {
- Set *set;
- char *result;
-} WaitData;
-
-static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
- DBusError error;
- WaitData *d = data;
-
- assert(connection);
- assert(message);
- assert(d);
-
- dbus_error_init(&error);
-
- 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_error("Warning! D-Bus connection terminated.");
- dbus_connection_close(connection);
-
- } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
- uint32_t id;
- const char *path, *result;
- dbus_bool_t success = true;
-
- if (dbus_message_get_args(message, &error,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_STRING, &result,
- DBUS_TYPE_INVALID)) {
- char *p;
-
- if ((p = set_remove(d->set, (char*) path)))
- free(p);
-
- if (*result)
- d->result = strdup(result);
-
- goto finish;
- }
-#ifndef LEGACY
- dbus_error_free(&error);
-
- if (dbus_message_get_args(message, &error,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_BOOLEAN, &success,
- DBUS_TYPE_INVALID)) {
- char *p;
-
- /* Compatibility with older systemd versions <
- * 19 during upgrades. This should be dropped
- * one day */
-
- if ((p = set_remove(d->set, (char*) path)))
- free(p);
-
- if (!success)
- d->result = strdup("failed");
-
- goto finish;
- }
-#endif
-
- log_error("Failed to parse message: %s", bus_error_message(&error));
- }
-
-finish:
- dbus_error_free(&error);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int enable_wait_for_jobs(DBusConnection *bus) {
- DBusError error;
-
- assert(bus);
-
- if (private_bus)
- return 0;
-
- dbus_error_init(&error);
- dbus_bus_add_match(bus,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Manager',"
- "member='JobRemoved',"
- "path='/org/freedesktop/systemd1'",
- &error);
-
- if (dbus_error_is_set(&error)) {
- log_error("Failed to add match: %s", bus_error_message(&error));
- dbus_error_free(&error);
- return -EIO;
- }
-
- /* This is slightly dirty, since we don't undo the match registrations. */
- return 0;
-}
-
-static int wait_for_jobs(DBusConnection *bus, Set *s) {
- int r;
- WaitData d;
-
- assert(bus);
- assert(s);
-
- zero(d);
- d.set = s;
-
- if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
- log_error("Failed to add filter.");
- r = -ENOMEM;
- goto finish;
- }
-
- while (!set_isempty(s) &&
- dbus_connection_read_write_dispatch(bus, -1))
- ;
-
- if (!arg_quiet && d.result) {
- if (streq(d.result, "timeout"))
- log_error("Job timed out.");
- else if (streq(d.result, "canceled"))
- log_error("Job canceled.");
- else if (streq(d.result, "dependency"))
- log_error("A dependency job failed. See system journal for details.");
- else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
- log_error("Job failed. See system journal and 'systemctl status' for details.");
- }
-
- if (streq_ptr(d.result, "timeout"))
- r = -ETIME;
- else if (streq_ptr(d.result, "canceled"))
- r = -ECANCELED;
- else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
- r = -EIO;
- else
- r = 0;
-
- free(d.result);
-
-finish:
- /* This is slightly dirty, since we don't undo the filter registration. */
-
- return r;
-}
-
-static int start_unit_one(
- DBusConnection *bus,
- const char *method,
- const char *name,
- const char *mode,
- DBusError *error,
- Set *s) {
-
- DBusMessage *m = NULL, *reply = NULL;
- const char *path;
- int r;
-
- assert(bus);
- assert(method);
- assert(name);
- assert(mode);
- assert(error);
- assert(arg_no_block || s);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- method))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
-
- if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
- /* There's always a fallback possible for
- * legacy actions. */
- r = -EADDRNOTAVAIL;
- goto finish;
- }
-
- log_error("Failed to issue method call: %s", bus_error_message(error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(error));
- r = -EIO;
- goto finish;
- }
-
- if (need_daemon_reload(bus, name))
- log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
- arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
-
- if (!arg_no_block) {
- char *p;
-
- if (!(p = strdup(path))) {
- log_error("Failed to duplicate path.");
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = set_put(s, p)) < 0) {
- free(p);
- log_error("Failed to add path to set.");
- goto finish;
- }
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- return r;
-}
-
-static enum action verb_to_action(const char *verb) {
- if (streq(verb, "halt"))
- return ACTION_HALT;
- else if (streq(verb, "poweroff"))
- return ACTION_POWEROFF;
- else if (streq(verb, "reboot"))
- return ACTION_REBOOT;
- else if (streq(verb, "kexec"))
- return ACTION_KEXEC;
- else if (streq(verb, "rescue"))
- return ACTION_RESCUE;
- else if (streq(verb, "emergency"))
- return ACTION_EMERGENCY;
- else if (streq(verb, "default"))
- return ACTION_DEFAULT;
- else if (streq(verb, "exit"))
- return ACTION_EXIT;
- else
- return ACTION_INVALID;
-}
-
-static int start_unit(DBusConnection *bus, char **args) {
-
- static const char * const table[_ACTION_MAX] = {
- [ACTION_HALT] = SPECIAL_HALT_TARGET,
- [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
- [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
- [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
- [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
- [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
- [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
- [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
- [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
- [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
- [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
- [ACTION_EXIT] = SPECIAL_EXIT_TARGET
- };
-
- int r, ret = 0;
- const char *method, *mode, *one_name;
- Set *s = NULL;
- DBusError error;
- char **name;
-
- dbus_error_init(&error);
-
- assert(bus);
-
- ask_password_agent_open_if_enabled();
-
- if (arg_action == ACTION_SYSTEMCTL) {
- method =
- streq(args[0], "stop") ||
- streq(args[0], "condstop") ? "StopUnit" :
- streq(args[0], "reload") ? "ReloadUnit" :
- streq(args[0], "restart") ? "RestartUnit" :
-
- streq(args[0], "try-restart") ||
- streq(args[0], "condrestart") ? "TryRestartUnit" :
-
- streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" :
-
- streq(args[0], "reload-or-try-restart") ||
- streq(args[0], "condreload") ||
-
- streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" :
- "StartUnit";
-
- mode =
- (streq(args[0], "isolate") ||
- streq(args[0], "rescue") ||
- streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
-
- one_name = table[verb_to_action(args[0])];
-
- } else {
- assert(arg_action < ELEMENTSOF(table));
- assert(table[arg_action]);
-
- method = "StartUnit";
-
- mode = (arg_action == ACTION_EMERGENCY ||
- arg_action == ACTION_RESCUE ||
- arg_action == ACTION_RUNLEVEL2 ||
- arg_action == ACTION_RUNLEVEL3 ||
- arg_action == ACTION_RUNLEVEL4 ||
- arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
-
- one_name = table[arg_action];
- }
-
- if (!arg_no_block) {
- if ((ret = enable_wait_for_jobs(bus)) < 0) {
- log_error("Could not watch jobs: %s", strerror(-ret));
- goto finish;
- }
-
- if (!(s = set_new(string_hash_func, string_compare_func))) {
- log_error("Failed to allocate set.");
- ret = -ENOMEM;
- goto finish;
- }
- }
-
- if (one_name) {
- if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
- goto finish;
- } else {
- STRV_FOREACH(name, args+1)
- if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
- ret = translate_bus_error_to_exit_status(r, &error);
- dbus_error_free(&error);
- }
- }
-
- if (!arg_no_block)
- if ((r = wait_for_jobs(bus, s)) < 0) {
- ret = r;
- goto finish;
- }
-
-finish:
- if (s)
- set_free_free(s);
-
- dbus_error_free(&error);
-
- return ret;
-}
-
-/* Ask systemd-logind, which might grant access to unprivileged users
- * through PolicyKit */
-static int reboot_with_logind(DBusConnection *bus, enum action a) {
-#ifdef HAVE_LOGIND
- const char *method;
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- dbus_bool_t interactive = true;
- int r;
-
- dbus_error_init(&error);
-
- polkit_agent_open_if_enabled();
-
- switch (a) {
-
- case ACTION_REBOOT:
- method = "Reboot";
- break;
-
- case ACTION_POWEROFF:
- method = "PowerOff";
- break;
-
- default:
- return -EINVAL;
- }
-
- m = dbus_message_new_method_call(
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- method);
- if (!m) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_BOOLEAN, &interactive,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- if (error_is_no_service(&error)) {
- log_debug("Failed to issue method call: %s", bus_error_message(&error));
- r = -ENOENT;
- goto finish;
- }
-
- if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
- log_debug("Failed to issue method call: %s", bus_error_message(&error));
- r = -EACCES;
- goto finish;
- }
-
- log_info("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-#else
- return -ENOSYS;
-#endif
-}
-
-static int start_special(DBusConnection *bus, char **args) {
- enum action a;
- int r;
-
- assert(bus);
- assert(args);
-
- a = verb_to_action(args[0]);
-
- if (arg_force >= 2 &&
- (a == ACTION_HALT ||
- a == ACTION_POWEROFF ||
- a == ACTION_REBOOT))
- halt_now(a);
-
- if (arg_force >= 1 &&
- (a == ACTION_HALT ||
- a == ACTION_POWEROFF ||
- a == ACTION_REBOOT ||
- a == ACTION_KEXEC ||
- a == ACTION_EXIT))
- return daemon_reload(bus, args);
-
- /* first try logind, to allow authentication with polkit */
- if (geteuid() != 0 &&
- (a == ACTION_POWEROFF ||
- a == ACTION_REBOOT)) {
- r = reboot_with_logind(bus, a);
- if (r >= 0)
- return r;
- }
-
- r = start_unit(bus, args);
- if (r >= 0)
- warn_wall(a);
-
- return r;
-}
-
-static int check_unit(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- const char
- *interface = "org.freedesktop.systemd1.Unit",
- *property = "ActiveState";
- int r = 3; /* According to LSB: "program is not running" */
- DBusError error;
- char **name;
-
- assert(bus);
- assert(args);
-
- dbus_error_init(&error);
-
- STRV_FOREACH(name, args+1) {
- const char *path = NULL;
- const char *state;
- DBusMessageIter iter, sub;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
- /* Hmm, cannot figure out anything about this unit... */
- if (!arg_quiet)
- puts("unknown");
-
- dbus_error_free(&error);
- dbus_message_unref(m);
- m = NULL;
- continue;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "Get"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_get_basic(&sub, &state);
-
- if (!arg_quiet)
- puts(state);
-
- if (streq(state, "active") || streq(state, "reloading"))
- r = 0;
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int kill_unit(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL;
- int r = 0;
- DBusError error;
- char **name;
-
- assert(bus);
- assert(args);
-
- dbus_error_init(&error);
-
- if (!arg_kill_who)
- arg_kill_who = "all";
-
- if (!arg_kill_mode)
- arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
-
- STRV_FOREACH(name, args+1) {
- DBusMessage *reply;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "KillUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_STRING, &arg_kill_who,
- DBUS_TYPE_STRING, &arg_kill_mode,
- DBUS_TYPE_INT32, &arg_signal,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- dbus_error_free(&error);
- r = -EIO;
- }
-
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-typedef struct ExecStatusInfo {
- char *name;
-
- char *path;
- char **argv;
-
- bool ignore;
-
- usec_t start_timestamp;
- usec_t exit_timestamp;
- pid_t pid;
- int code;
- int status;
-
- LIST_FIELDS(struct ExecStatusInfo, exec);
-} ExecStatusInfo;
-
-static void exec_status_info_free(ExecStatusInfo *i) {
- assert(i);
-
- free(i->name);
- free(i->path);
- strv_free(i->argv);
- free(i);
-}
-
-static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
- uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
- DBusMessageIter sub2, sub3;
- const char*path;
- unsigned n;
- uint32_t pid;
- int32_t code, status;
- dbus_bool_t ignore;
-
- assert(i);
- assert(i);
-
- if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
- return -EIO;
-
- dbus_message_iter_recurse(sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
- return -EIO;
-
- if (!(i->path = strdup(path)))
- return -ENOMEM;
-
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
- return -EIO;
-
- n = 0;
- dbus_message_iter_recurse(&sub2, &sub3);
- while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
- assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
- dbus_message_iter_next(&sub3);
- n++;
- }
-
-
- if (!(i->argv = new0(char*, n+1)))
- return -ENOMEM;
-
- n = 0;
- dbus_message_iter_recurse(&sub2, &sub3);
- while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
- const char *s;
-
- assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
- dbus_message_iter_get_basic(&sub3, &s);
- dbus_message_iter_next(&sub3);
-
- if (!(i->argv[n++] = strdup(s)))
- return -ENOMEM;
- }
-
- if (!dbus_message_iter_next(&sub2) ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp_monotonic, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp_monotonic, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
- return -EIO;
-
- i->ignore = ignore;
- i->start_timestamp = (usec_t) start_timestamp;
- i->exit_timestamp = (usec_t) exit_timestamp;
- i->pid = (pid_t) pid;
- i->code = code;
- i->status = status;
-
- return 0;
-}
-
-typedef struct UnitStatusInfo {
- const char *id;
- const char *load_state;
- const char *active_state;
- const char *sub_state;
- const char *unit_file_state;
-
- const char *description;
- const char *following;
-
- const char *path;
- const char *default_control_group;
-
- const char *load_error;
- const char *result;
-
- usec_t inactive_exit_timestamp;
- usec_t inactive_exit_timestamp_monotonic;
- usec_t active_enter_timestamp;
- usec_t active_exit_timestamp;
- usec_t inactive_enter_timestamp;
-
- bool need_daemon_reload;
-
- /* Service */
- pid_t main_pid;
- pid_t control_pid;
- const char *status_text;
- bool running:1;
-#ifdef HAVE_SYSV_COMPAT
- bool is_sysv:1;
-#endif
-
- usec_t start_timestamp;
- usec_t exit_timestamp;
-
- int exit_code, exit_status;
-
- usec_t condition_timestamp;
- bool condition_result;
-
- /* Socket */
- unsigned n_accepted;
- unsigned n_connections;
- bool accept;
-
- /* Device */
- const char *sysfs_path;
-
- /* Mount, Automount */
- const char *where;
-
- /* Swap */
- const char *what;
-
- LIST_HEAD(ExecStatusInfo, exec);
-} UnitStatusInfo;
-
-static void print_status_info(UnitStatusInfo *i) {
- ExecStatusInfo *p;
- const char *on, *off, *ss;
- usec_t timestamp;
- char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
- char since2[FORMAT_TIMESTAMP_MAX], *s2;
-
- assert(i);
-
- /* This shows pretty information about a unit. See
- * print_property() for a low-level property printer */
-
- printf("%s", strna(i->id));
-
- if (i->description && !streq_ptr(i->id, i->description))
- printf(" - %s", i->description);
-
- printf("\n");
-
- if (i->following)
- printf("\t Follow: unit currently follows state of %s\n", i->following);
-
- if (streq_ptr(i->load_state, "error")) {
- on = ansi_highlight_red(true);
- off = ansi_highlight_red(false);
- } else
- on = off = "";
-
- if (i->load_error)
- printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
- else if (i->path && i->unit_file_state)
- printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
- else if (i->path)
- printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
- else
- printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
-
- ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
-
- if (streq_ptr(i->active_state, "failed")) {
- on = ansi_highlight_red(true);
- off = ansi_highlight_red(false);
- } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
- on = ansi_highlight_green(true);
- off = ansi_highlight_green(false);
- } else
- on = off = "";
-
- if (ss)
- printf("\t Active: %s%s (%s)%s",
- on,
- strna(i->active_state),
- ss,
- off);
- else
- printf("\t Active: %s%s%s",
- on,
- strna(i->active_state),
- off);
-
- if (!isempty(i->result) && !streq(i->result, "success"))
- printf(" (Result: %s)", i->result);
-
- timestamp = (streq_ptr(i->active_state, "active") ||
- streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
- (streq_ptr(i->active_state, "inactive") ||
- streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
- streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
- i->active_exit_timestamp;
-
- s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
- s2 = format_timestamp(since2, sizeof(since2), timestamp);
-
- if (s1)
- printf(" since %s; %s\n", s2, s1);
- else if (s2)
- printf(" since %s\n", s2);
- else
- printf("\n");
-
- if (!i->condition_result && i->condition_timestamp > 0) {
- s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp);
- s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
-
- if (s1)
- printf("\t start condition failed at %s; %s\n", s2, s1);
- else if (s2)
- printf("\t start condition failed at %s\n", s2);
- }
-
- if (i->sysfs_path)
- printf("\t Device: %s\n", i->sysfs_path);
- if (i->where)
- printf("\t Where: %s\n", i->where);
- if (i->what)
- printf("\t What: %s\n", i->what);
-
- if (i->accept)
- printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
-
- LIST_FOREACH(exec, p, i->exec) {
- char *t;
- bool good;
-
- /* Only show exited processes here */
- if (p->code == 0)
- continue;
-
- t = strv_join(p->argv, " ");
- printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
- free(t);
-
-#ifdef HAVE_SYSV_COMPAT
- if (i->is_sysv)
- good = is_clean_exit_lsb(p->code, p->status);
- else
-#endif
- good = is_clean_exit(p->code, p->status);
-
- if (!good) {
- on = ansi_highlight_red(true);
- off = ansi_highlight_red(false);
- } else
- on = off = "";
-
- printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
-
- if (p->code == CLD_EXITED) {
- const char *c;
-
- printf("status=%i", p->status);
-
-#ifdef HAVE_SYSV_COMPAT
- if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
- if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
-#endif
- printf("/%s", c);
-
- } else
- printf("signal=%s", signal_to_string(p->status));
-
- printf(")%s\n", off);
-
- if (i->main_pid == p->pid &&
- i->start_timestamp == p->start_timestamp &&
- i->exit_timestamp == p->start_timestamp)
- /* Let's not show this twice */
- i->main_pid = 0;
-
- if (p->pid == i->control_pid)
- i->control_pid = 0;
- }
-
- if (i->main_pid > 0 || i->control_pid > 0) {
- printf("\t");
-
- if (i->main_pid > 0) {
- printf("Main PID: %u", (unsigned) i->main_pid);
-
- if (i->running) {
- char *t = NULL;
- get_process_comm(i->main_pid, &t);
- if (t) {
- printf(" (%s)", t);
- free(t);
- }
- } else if (i->exit_code > 0) {
- printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
-
- if (i->exit_code == CLD_EXITED) {
- const char *c;
-
- printf("status=%i", i->exit_status);
-
-#ifdef HAVE_SYSV_COMPAT
- if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
- if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
-#endif
- printf("/%s", c);
-
- } else
- printf("signal=%s", signal_to_string(i->exit_status));
- printf(")");
- }
- }
-
- if (i->main_pid > 0 && i->control_pid > 0)
- printf(";");
-
- if (i->control_pid > 0) {
- char *t = NULL;
-
- printf(" Control: %u", (unsigned) i->control_pid);
-
- get_process_comm(i->control_pid, &t);
- if (t) {
- printf(" (%s)", t);
- free(t);
- }
- }
-
- printf("\n");
- }
-
- if (i->status_text)
- printf("\t Status: \"%s\"\n", i->status_text);
-
- if (i->default_control_group) {
- unsigned c;
-
- printf("\t CGroup: %s\n", i->default_control_group);
-
- if (arg_transport != TRANSPORT_SSH) {
- if ((c = columns()) > 18)
- c -= 18;
- else
- c = 0;
-
- show_cgroup_by_path(i->default_control_group, "\t\t ", c, false);
- }
- }
-
- if (i->id && arg_transport != TRANSPORT_SSH) {
- printf("\n");
- show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
- }
-
- if (i->need_daemon_reload)
- printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
- ansi_highlight_red(true),
- ansi_highlight_red(false),
- arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
-}
-
-static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
-
- assert(name);
- assert(iter);
- assert(i);
-
- switch (dbus_message_iter_get_arg_type(iter)) {
-
- case DBUS_TYPE_STRING: {
- const char *s;
-
- dbus_message_iter_get_basic(iter, &s);
-
- if (!isempty(s)) {
- if (streq(name, "Id"))
- i->id = s;
- else if (streq(name, "LoadState"))
- i->load_state = s;
- else if (streq(name, "ActiveState"))
- i->active_state = s;
- else if (streq(name, "SubState"))
- i->sub_state = s;
- else if (streq(name, "Description"))
- i->description = s;
- else if (streq(name, "FragmentPath"))
- i->path = s;
-#ifdef HAVE_SYSV_COMPAT
- else if (streq(name, "SysVPath")) {
- i->is_sysv = true;
- i->path = s;
- }
-#endif
- else if (streq(name, "DefaultControlGroup"))
- i->default_control_group = s;
- else if (streq(name, "StatusText"))
- i->status_text = s;
- else if (streq(name, "SysFSPath"))
- i->sysfs_path = s;
- else if (streq(name, "Where"))
- i->where = s;
- else if (streq(name, "What"))
- i->what = s;
- else if (streq(name, "Following"))
- i->following = s;
- else if (streq(name, "UnitFileState"))
- i->unit_file_state = s;
- else if (streq(name, "Result"))
- i->result = s;
- }
-
- break;
- }
-
- case DBUS_TYPE_BOOLEAN: {
- dbus_bool_t b;
-
- dbus_message_iter_get_basic(iter, &b);
-
- if (streq(name, "Accept"))
- i->accept = b;
- else if (streq(name, "NeedDaemonReload"))
- i->need_daemon_reload = b;
- else if (streq(name, "ConditionResult"))
- i->condition_result = b;
-
- break;
- }
-
- case DBUS_TYPE_UINT32: {
- uint32_t u;
-
- dbus_message_iter_get_basic(iter, &u);
-
- if (streq(name, "MainPID")) {
- if (u > 0) {
- i->main_pid = (pid_t) u;
- i->running = true;
- }
- } else if (streq(name, "ControlPID"))
- i->control_pid = (pid_t) u;
- else if (streq(name, "ExecMainPID")) {
- if (u > 0)
- i->main_pid = (pid_t) u;
- } else if (streq(name, "NAccepted"))
- i->n_accepted = u;
- else if (streq(name, "NConnections"))
- i->n_connections = u;
-
- break;
- }
-
- case DBUS_TYPE_INT32: {
- int32_t j;
-
- dbus_message_iter_get_basic(iter, &j);
-
- if (streq(name, "ExecMainCode"))
- i->exit_code = (int) j;
- else if (streq(name, "ExecMainStatus"))
- i->exit_status = (int) j;
-
- break;
- }
-
- case DBUS_TYPE_UINT64: {
- uint64_t u;
-
- dbus_message_iter_get_basic(iter, &u);
-
- if (streq(name, "ExecMainStartTimestamp"))
- i->start_timestamp = (usec_t) u;
- else if (streq(name, "ExecMainExitTimestamp"))
- i->exit_timestamp = (usec_t) u;
- else if (streq(name, "ActiveEnterTimestamp"))
- i->active_enter_timestamp = (usec_t) u;
- else if (streq(name, "InactiveEnterTimestamp"))
- i->inactive_enter_timestamp = (usec_t) u;
- else if (streq(name, "InactiveExitTimestamp"))
- i->inactive_exit_timestamp = (usec_t) u;
- else if (streq(name, "InactiveExitTimestampMonotonic"))
- i->inactive_exit_timestamp_monotonic = (usec_t) u;
- else if (streq(name, "ActiveExitTimestamp"))
- i->active_exit_timestamp = (usec_t) u;
- else if (streq(name, "ConditionTimestamp"))
- i->condition_timestamp = (usec_t) u;
-
- break;
- }
-
- case DBUS_TYPE_ARRAY: {
-
- if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
- startswith(name, "Exec")) {
- DBusMessageIter sub;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- ExecStatusInfo *info;
- int r;
-
- if (!(info = new0(ExecStatusInfo, 1)))
- return -ENOMEM;
-
- if (!(info->name = strdup(name))) {
- free(info);
- return -ENOMEM;
- }
-
- if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
- free(info);
- return r;
- }
-
- LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
-
- dbus_message_iter_next(&sub);
- }
- }
-
- break;
- }
-
- case DBUS_TYPE_STRUCT: {
-
- if (streq(name, "LoadError")) {
- DBusMessageIter sub;
- const char *n, *message;
- int r;
-
- dbus_message_iter_recurse(iter, &sub);
-
- r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
- if (r < 0)
- return r;
-
- r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
- if (r < 0)
- return r;
-
- if (!isempty(message))
- i->load_error = message;
- }
-
- break;
- }
- }
-
- return 0;
-}
-
-static int print_property(const char *name, DBusMessageIter *iter) {
- assert(name);
- assert(iter);
-
- /* This is a low-level property printer, see
- * print_status_info() for the nicer output */
-
- if (arg_property && !strv_find(arg_property, name))
- return 0;
-
- switch (dbus_message_iter_get_arg_type(iter)) {
-
- case DBUS_TYPE_STRUCT: {
- DBusMessageIter sub;
- dbus_message_iter_recurse(iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
- uint32_t u;
-
- dbus_message_iter_get_basic(&sub, &u);
-
- if (u)
- printf("%s=%u\n", name, (unsigned) u);
- else if (arg_all)
- printf("%s=\n", name);
-
- return 0;
- } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
- const char *s;
-
- dbus_message_iter_get_basic(&sub, &s);
-
- if (arg_all || s[0])
- printf("%s=%s\n", name, s);
-
- return 0;
- } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
- const char *a = NULL, *b = NULL;
-
- if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
- bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
-
- if (arg_all || !isempty(a) || !isempty(b))
- printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
-
- return 0;
- }
-
- break;
- }
-
- case DBUS_TYPE_ARRAY:
-
- if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
- DBusMessageIter sub, sub2;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- const char *path;
- dbus_bool_t ignore;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0)
- printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
-
- } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
- DBusMessageIter sub, sub2;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- const char *type, *path;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
- printf("%s=%s\n", type, path);
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
-
- } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
- DBusMessageIter sub, sub2;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- const char *base;
- uint64_t value, next_elapse;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
- char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
-
- printf("%s={ value=%s ; next_elapse=%s }\n",
- base,
- format_timespan(timespan1, sizeof(timespan1), value),
- format_timespan(timespan2, sizeof(timespan2), next_elapse));
- }
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
-
- } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
- DBusMessageIter sub, sub2;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- const char *controller, *attr, *value;
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
-
- printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
- controller,
- attr,
- value);
- }
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
-
- } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
- DBusMessageIter sub;
-
- dbus_message_iter_recurse(iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
- ExecStatusInfo info;
-
- zero(info);
- if (exec_status_info_deserialize(&sub, &info) >= 0) {
- char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
- char *t;
-
- t = strv_join(info.argv, " ");
-
- printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
- name,
- strna(info.path),
- strna(t),
- yes_no(info.ignore),
- strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
- strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
- (unsigned) info. pid,
- sigchld_code_to_string(info.code),
- info.status,
- info.code == CLD_EXITED ? "" : "/",
- strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
-
- free(t);
- }
-
- free(info.path);
- strv_free(info.argv);
-
- dbus_message_iter_next(&sub);
- }
-
- return 0;
- }
-
- break;
- }
-
- if (generic_print_property(name, iter, arg_all) > 0)
- return 0;
-
- if (arg_all)
- printf("%s=[unprintable]\n", name);
-
- return 0;
-}
-
-static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
- DBusMessage *m = NULL, *reply = NULL;
- const char *interface = "";
- int r;
- DBusError error;
- DBusMessageIter iter, sub, sub2, sub3;
- UnitStatusInfo info;
- ExecStatusInfo *p;
-
- assert(bus);
- assert(path);
- assert(new_line);
-
- zero(info);
- dbus_error_init(&error);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (*new_line)
- printf("\n");
-
- *new_line = true;
-
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *name;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub2, &sub3);
-
- if (show_properties)
- r = print_property(name, &sub3);
- else
- r = status_property(name, &sub3, &info);
-
- if (r < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_next(&sub);
- }
-
- r = 0;
-
- if (!show_properties)
- print_status_info(&info);
-
- if (!streq_ptr(info.active_state, "active") &&
- !streq_ptr(info.active_state, "reloading") &&
- streq(verb, "status"))
- /* According to LSB: "program not running" */
- r = 3;
-
- while ((p = info.exec)) {
- LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
- exec_status_info_free(p);
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int show(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- int r, ret = 0;
- DBusError error;
- bool show_properties, new_line = false;
- char **name;
-
- assert(bus);
- assert(args);
-
- dbus_error_init(&error);
-
- show_properties = !streq(args[0], "status");
-
- if (show_properties)
- pager_open_if_enabled();
-
- if (show_properties && strv_length(args) <= 1) {
- /* If not argument is specified inspect the manager
- * itself */
-
- ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
- goto finish;
- }
-
- STRV_FOREACH(name, args+1) {
- const char *path = NULL;
- uint32_t id;
-
- if (safe_atou32(*name, &id) < 0) {
-
- /* Interpret as unit name */
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "LoadUnit"))) {
- log_error("Could not allocate message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
- if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- ret = -EIO;
- goto finish;
- }
-
- dbus_error_free(&error);
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit"))) {
- log_error("Could not allocate message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
-
- if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
- ret = 4; /* According to LSB: "program or service status is unknown" */
- else
- ret = -EIO;
- goto finish;
- }
- }
-
- } else if (show_properties) {
-
- /* Interpret as job id */
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetJob"))) {
- log_error("Could not allocate message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- ret = -EIO;
- goto finish;
- }
- } else {
-
- /* Interpret as PID */
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnitByPID"))) {
- log_error("Could not allocate message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_UINT32, &id,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- ret = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- ret = -EIO;
- goto finish;
- }
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- ret = -EIO;
- goto finish;
- }
-
- if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
- ret = r;
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return ret;
-}
-
-static int dump(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- const char *text;
-
- dbus_error_init(&error);
-
- pager_open_if_enabled();
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "Dump"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_STRING, &text,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- fputs(text, stdout);
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int snapshot(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- const char *name = "", *path, *id;
- dbus_bool_t cleanup = FALSE;
- DBusMessageIter iter, sub;
- const char
- *interface = "org.freedesktop.systemd1.Unit",
- *property = "Id";
-
- dbus_error_init(&error);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "CreateSnapshot"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (strv_length(args) > 1)
- name = args[1];
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_BOOLEAN, &cleanup,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "Get"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_get_basic(&sub, &id);
-
- if (!arg_quiet)
- puts(id);
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int delete_snapshot(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- int r;
- DBusError error;
- char **name;
-
- assert(bus);
- assert(args);
-
- dbus_error_init(&error);
-
- STRV_FOREACH(name, args+1) {
- const char *path = NULL;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Snapshot",
- "Remove"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int daemon_reload(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- const char *method;
-
- dbus_error_init(&error);
-
- if (arg_action == ACTION_RELOAD)
- method = "Reload";
- else if (arg_action == ACTION_REEXEC)
- method = "Reexecute";
- else {
- assert(arg_action == ACTION_SYSTEMCTL);
-
- method =
- streq(args[0], "clear-jobs") ||
- streq(args[0], "cancel") ? "ClearJobs" :
- streq(args[0], "daemon-reexec") ? "Reexecute" :
- streq(args[0], "reset-failed") ? "ResetFailed" :
- streq(args[0], "halt") ? "Halt" :
- streq(args[0], "poweroff") ? "PowerOff" :
- streq(args[0], "reboot") ? "Reboot" :
- streq(args[0], "kexec") ? "KExec" :
- streq(args[0], "exit") ? "Exit" :
- /* "daemon-reload" */ "Reload";
- }
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- method))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
- if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
- /* There's always a fallback possible for
- * legacy actions. */
- r = -EADDRNOTAVAIL;
- goto finish;
- }
-
- if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) {
- /* On reexecution, we expect a disconnect, not
- * a reply */
- r = 0;
- goto finish;
- }
-
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int reset_failed(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL;
- int r;
- DBusError error;
- char **name;
-
- assert(bus);
- dbus_error_init(&error);
-
- if (strv_length(args) <= 1)
- return daemon_reload(bus, args);
-
- STRV_FOREACH(name, args+1) {
- DBusMessage *reply;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ResetFailedUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int show_enviroment(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- DBusMessageIter iter, sub, sub2;
- int r;
- const char
- *interface = "org.freedesktop.systemd1.Manager",
- *property = "Environment";
-
- dbus_error_init(&error);
-
- pager_open_if_enabled();
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.DBus.Properties",
- "Get"))) {
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
- const char *text;
-
- if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_get_basic(&sub2, &text);
- printf("%s\n", text);
-
- dbus_message_iter_next(&sub2);
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int set_environment(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int r;
- const char *method;
- DBusMessageIter iter, sub;
- char **name;
-
- dbus_error_init(&error);
-
- method = streq(args[0], "set-environment")
- ? "SetEnvironment"
- : "UnsetEnvironment";
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- method))) {
-
- log_error("Could not allocate message.");
- return -ENOMEM;
- }
-
- dbus_message_iter_init_append(m, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- STRV_FOREACH(name, args+1)
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_iter_close_container(&iter, &sub)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- r = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int enable_sysv_units(char **args) {
- int r = 0;
-
-#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
- const char *verb = args[0];
- unsigned f = 1, t = 1;
- LookupPaths paths;
-
- if (arg_scope != UNIT_FILE_SYSTEM)
- return 0;
-
- if (!streq(verb, "enable") &&
- !streq(verb, "disable") &&
- !streq(verb, "is-enabled"))
- return 0;
-
- /* Processes all SysV units, and reshuffles the array so that
- * afterwards only the native units remain */
-
- zero(paths);
- r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
- if (r < 0)
- return r;
-
- r = 0;
-
- for (f = 1; args[f]; f++) {
- const char *name;
- char *p;
- bool found_native = false, found_sysv;
- unsigned c = 1;
- const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
- char **k, *l, *q = NULL;
- int j;
- pid_t pid;
- siginfo_t status;
-
- name = args[f];
-
- if (!endswith(name, ".service"))
- continue;
-
- if (path_is_absolute(name))
- continue;
-
- STRV_FOREACH(k, paths.unit_path) {
- p = NULL;
-
- if (!isempty(arg_root))
- asprintf(&p, "%s/%s/%s", arg_root, *k, name);
- else
- asprintf(&p, "%s/%s", *k, name);
-
- if (!p) {
- log_error("No memory");
- r = -ENOMEM;
- goto finish;
- }
-
- found_native = access(p, F_OK) >= 0;
- free(p);
-
- if (found_native)
- break;
- }
-
- if (found_native)
- continue;
-
- p = NULL;
- if (!isempty(arg_root))
- asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
- else
- asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
- if (!p) {
- log_error("No memory");
- r = -ENOMEM;
- goto finish;
- }
-
- p[strlen(p) - sizeof(".service") + 1] = 0;
- found_sysv = access(p, F_OK) >= 0;
-
- if (!found_sysv) {
- free(p);
- continue;
- }
-
- /* Mark this entry, so that we don't try enabling it as native unit */
- args[f] = (char*) "";
-
- log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
-
- if (!isempty(arg_root))
- argv[c++] = q = strappend("--root=", arg_root);
-
- argv[c++] = file_name_from_path(p);
- argv[c++] =
- streq(verb, "enable") ? "on" :
- streq(verb, "disable") ? "off" : "--level=5";
- argv[c] = NULL;
-
- l = strv_join((char**)argv, " ");
- if (!l) {
- log_error("No memory.");
- free(q);
- free(p);
- r = -ENOMEM;
- goto finish;
- }
-
- log_info("Executing %s", l);
- free(l);
-
- pid = fork();
- if (pid < 0) {
- log_error("Failed to fork: %m");
- free(p);
- free(q);
- r = -errno;
- goto finish;
- } else if (pid == 0) {
- /* Child */
-
- execv(argv[0], (char**) argv);
- _exit(EXIT_FAILURE);
- }
-
- free(p);
- free(q);
-
- j = wait_for_terminate(pid, &status);
- if (j < 0) {
- log_error("Failed to wait for child: %s", strerror(-r));
- r = j;
- goto finish;
- }
-
- if (status.si_code == CLD_EXITED) {
- if (streq(verb, "is-enabled")) {
- if (status.si_status == 0) {
- if (!arg_quiet)
- puts("enabled");
- r = 1;
- } else {
- if (!arg_quiet)
- puts("disabled");
- }
-
- } else if (status.si_status != 0) {
- r = -EINVAL;
- goto finish;
- }
- } else {
- r = -EPROTO;
- goto finish;
- }
- }
-
-finish:
- lookup_paths_free(&paths);
-
- /* Drop all SysV units */
- for (f = 1, t = 1; args[f]; f++) {
-
- if (isempty(args[f]))
- continue;
-
- args[t++] = args[f];
- }
-
- args[t] = NULL;
-
-#endif
- return r;
-}
-
-static int enable_unit(DBusConnection *bus, char **args) {
- const char *verb = args[0];
- UnitFileChange *changes = NULL;
- unsigned n_changes = 0, i;
- int carries_install_info = -1;
- DBusMessage *m = NULL, *reply = NULL;
- int r;
- DBusError error;
-
- r = enable_sysv_units(args);
- if (r < 0)
- return r;
-
- if (!args[1])
- return 0;
-
- dbus_error_init(&error);
-
- if (!bus || avoid_bus()) {
- if (streq(verb, "enable")) {
- r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(verb, "disable"))
- r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
- else if (streq(verb, "reenable")) {
- r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(verb, "link"))
- r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
- else if (streq(verb, "preset")) {
- r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
- carries_install_info = r;
- } else if (streq(verb, "mask"))
- r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
- else if (streq(verb, "unmask"))
- r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
- else
- assert_not_reached("Unknown verb");
-
- if (r < 0) {
- log_error("Operation failed: %s", strerror(-r));
- goto finish;
- }
-
- if (!arg_quiet) {
- for (i = 0; i < n_changes; i++) {
- if (changes[i].type == UNIT_FILE_SYMLINK)
- log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
- else
- log_info("rm '%s'", changes[i].path);
- }
- }
-
- } else {
- const char *method;
- bool send_force = true, expect_carries_install_info = false;
- dbus_bool_t a, b;
- DBusMessageIter iter, sub, sub2;
-
- if (streq(verb, "enable")) {
- method = "EnableUnitFiles";
- expect_carries_install_info = true;
- } else if (streq(verb, "disable")) {
- method = "DisableUnitFiles";
- send_force = false;
- } else if (streq(verb, "reenable")) {
- method = "ReenableUnitFiles";
- expect_carries_install_info = true;
- } else if (streq(verb, "link"))
- method = "LinkUnitFiles";
- else if (streq(verb, "preset")) {
- method = "PresetUnitFiles";
- expect_carries_install_info = true;
- } else if (streq(verb, "mask"))
- method = "MaskUnitFiles";
- else if (streq(verb, "unmask")) {
- method = "UnmaskUnitFiles";
- send_force = false;
- } else
- assert_not_reached("Unknown verb");
-
- m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- method);
- if (!m) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_iter_init_append(m, &iter);
-
- r = bus_append_strv_iter(&iter, args+1);
- if (r < 0) {
- log_error("Failed to append unit files.");
- goto finish;
- }
-
- a = arg_runtime;
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
- log_error("Failed to append runtime boolean.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (send_force) {
- b = arg_force;
-
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
- log_error("Failed to append force boolean.");
- r = -ENOMEM;
- goto finish;
- }
- }
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter)) {
- log_error("Failed to initialize iterator.");
- goto finish;
- }
-
- if (expect_carries_install_info) {
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
- if (r < 0) {
- log_error("Failed to parse reply.");
- goto finish;
- }
-
- carries_install_info = b;
- }
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
- while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
- const char *type, *path, *source;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&sub, &sub2);
-
- if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
- bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- if (!arg_quiet) {
- if (streq(type, "symlink"))
- log_info("ln -s '%s' '%s'", source, path);
- else
- log_info("rm '%s'", path);
- }
-
- dbus_message_iter_next(&sub);
- }
-
- /* Try to reload if enabeld */
- if (!arg_no_reload)
- r = daemon_reload(bus, args);
- }
-
- if (carries_install_info == 0)
- log_warning("Warning: unit files do not carry install information. No operation executed.");
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- unit_file_changes_free(changes, n_changes);
-
- dbus_error_free(&error);
- return r;
-}
-
-static int unit_is_enabled(DBusConnection *bus, char **args) {
- DBusError error;
- int r;
- DBusMessage *m = NULL, *reply = NULL;
- bool enabled;
- char **name;
-
- dbus_error_init(&error);
-
- r = enable_sysv_units(args);
- if (r < 0)
- return r;
-
- enabled = r > 0;
-
- if (!bus || avoid_bus()) {
-
- STRV_FOREACH(name, args+1) {
- UnitFileState state;
-
- state = unit_file_get_state(arg_scope, arg_root, *name);
- if (state < 0) {
- r = state;
- goto finish;
- }
-
- if (state == UNIT_FILE_ENABLED ||
- state == UNIT_FILE_ENABLED_RUNTIME ||
- state == UNIT_FILE_STATIC)
- enabled = true;
-
- if (!arg_quiet)
- puts(unit_file_state_to_string(state));
- }
-
- } else {
- STRV_FOREACH(name, args+1) {
- const char *s;
-
- m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnitFileState");
- if (!m) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, name,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_STRING, &s,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
-
- if (streq(s, "enabled") ||
- streq(s, "enabled-runtime") ||
- streq(s, "static"))
- enabled = true;
-
- if (!arg_quiet)
- puts(s);
- }
- }
-
- r = enabled ? 0 : 1;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
- return r;
-}
-
-static int systemctl_help(void) {
-
- pager_open_if_enabled();
-
- printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Query or send control commands to the systemd manager.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -t --type=TYPE List only units of a particular type\n"
- " -p --property=NAME Show only properties by this name\n"
- " -a --all Show all units/properties, including dead/empty ones\n"
- " --failed Show only failed units\n"
- " --full Don't ellipsize unit names on output\n"
- " --fail When queueing a new job, fail if conflicting jobs are\n"
- " pending\n"
- " --ignore-dependencies\n"
- " When queueing a new job, ignore all its dependencies\n"
- " --kill-who=WHO Who to send signal to\n"
- " -s --signal=SIGNAL Which signal to send\n"
- " -H --host=[USER@]HOST\n"
- " Show information for remote host\n"
- " -P --privileged Acquire privileges before execution\n"
- " -q --quiet Suppress output\n"
- " --no-block Do not wait until operation finished\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n"
- " --no-reload When enabling/disabling unit files, don't reload daemon\n"
- " configuration\n"
- " --no-legend Do not print a legend (column headers and hints)\n"
- " --no-pager Do not pipe output into a pager\n"
- " --no-ask-password\n"
- " Do not ask for system passwords\n"
- " --order When generating graph for dot, show only order\n"
- " --require When generating graph for dot, show only requirement\n"
- " --system Connect to system manager\n"
- " --user Connect to user service manager\n"
- " --global Enable/disable unit files globally\n"
- " -f --force When enabling unit files, override existing symlinks\n"
- " When shutting down, execute action immediately\n"
- " --root=PATH Enable unit files in the specified root directory\n"
- " --runtime Enable unit files only temporarily until next reboot\n"
- " -n --lines=INTEGER Journal entries to show\n"
- " --follow Follow journal\n"
- " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
- " verbose, export, json, cat)\n\n"
- "Unit Commands:\n"
- " list-units List loaded units\n"
- " start [NAME...] Start (activate) one or more units\n"
- " stop [NAME...] Stop (deactivate) one or more units\n"
- " reload [NAME...] Reload one or more units\n"
- " restart [NAME...] Start or restart one or more units\n"
- " try-restart [NAME...] Restart one or more units if active\n"
- " reload-or-restart [NAME...] Reload one or more units is possible,\n"
- " otherwise start or restart\n"
- " reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
- " otherwise restart if active\n"
- " isolate [NAME] Start one unit and stop all others\n"
- " kill [NAME...] Send signal to processes of a unit\n"
- " is-active [NAME...] Check whether units are active\n"
- " status [NAME...|PID...] Show runtime status of one or more units\n"
- " show [NAME...|JOB...] Show properties of one or more\n"
- " units/jobs or the manager\n"
- " reset-failed [NAME...] Reset failed state for all, one, or more\n"
- " units\n"
- " load [NAME...] Load one or more units\n\n"
- "Unit File Commands:\n"
- " list-unit-files List installed unit files\n"
- " enable [NAME...] Enable one or more unit files\n"
- " disable [NAME...] Disable one or more unit files\n"
- " reenable [NAME...] Reenable one or more unit files\n"
- " preset [NAME...] Enable/disable one or more unit files\n"
- " based on preset configuration\n"
- " mask [NAME...] Mask one or more units\n"
- " unmask [NAME...] Unmask one or more units\n"
- " link [PATH...] Link one or more units files into\n"
- " the search path\n"
- " is-enabled [NAME...] Check whether unit files are enabled\n\n"
- "Job Commands:\n"
- " list-jobs List jobs\n"
- " cancel [JOB...] Cancel all, one, or more jobs\n\n"
- "Status Commands:\n"
- " dump Dump server status\n"
- " dot Dump dependency graph for dot(1)\n\n"
- "Snapshot Commands:\n"
- " snapshot [NAME] Create a snapshot\n"
- " delete [NAME...] Remove one or more snapshots\n\n"
- "Environment Commands:\n"
- " show-environment Dump environment\n"
- " set-environment [NAME=VALUE...] Set one or more environment variables\n"
- " unset-environment [NAME...] Unset one or more environment variables\n\n"
- "Manager Lifecycle Commands:\n"
- " daemon-reload Reload systemd manager configuration\n"
- " daemon-reexec Reexecute systemd manager\n\n"
- "System Commands:\n"
- " default Enter system default mode\n"
- " rescue Enter system rescue mode\n"
- " emergency Enter system emergency mode\n"
- " halt Shut down and halt the system\n"
- " poweroff Shut down and power-off the system\n"
- " reboot Shut down and reboot the system\n"
- " kexec Shut down and reboot the system with kexec\n"
- " exit Ask for user instance termination\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int halt_help(void) {
-
- printf("%s [OPTIONS...]\n\n"
- "%s the system.\n\n"
- " --help Show this help\n"
- " --halt Halt the machine\n"
- " -p --poweroff Switch off the machine\n"
- " --reboot Reboot the machine\n"
- " -f --force Force immediate halt/power-off/reboot\n"
- " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
- " -d --no-wtmp Don't write wtmp record\n"
- " -n --no-sync Don't sync before halt/power-off/reboot\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n",
- program_invocation_short_name,
- arg_action == ACTION_REBOOT ? "Reboot" :
- arg_action == ACTION_POWEROFF ? "Power off" :
- "Halt");
-
- return 0;
-}
-
-static int shutdown_help(void) {
-
- printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
- "Shut down the system.\n\n"
- " --help Show this help\n"
- " -H --halt Halt the machine\n"
- " -P --poweroff Power-off the machine\n"
- " -r --reboot Reboot the machine\n"
- " -h Equivalent to --poweroff, overriden by --halt\n"
- " -k Don't halt/power-off/reboot, just send warnings\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n"
- " -c Cancel a pending shutdown\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int telinit_help(void) {
-
- printf("%s [OPTIONS...] {COMMAND}\n\n"
- "Send control commands to the init daemon.\n\n"
- " --help Show this help\n"
- " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
- "Commands:\n"
- " 0 Power-off the machine\n"
- " 6 Reboot the machine\n"
- " 2, 3, 4, 5 Start runlevelX.target unit\n"
- " 1, s, S Enter rescue mode\n"
- " q, Q Reload init daemon configuration\n"
- " u, U Reexecute init daemon\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int runlevel_help(void) {
-
- printf("%s [OPTIONS...]\n\n"
- "Prints the previous and current runlevel of the init system.\n\n"
- " --help Show this help\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int systemctl_parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_FAIL = 0x100,
- ARG_IGNORE_DEPENDENCIES,
- ARG_VERSION,
- ARG_USER,
- ARG_SYSTEM,
- ARG_GLOBAL,
- ARG_NO_BLOCK,
- ARG_NO_LEGEND,
- ARG_NO_PAGER,
- ARG_NO_WALL,
- ARG_ORDER,
- ARG_REQUIRE,
- ARG_ROOT,
- ARG_FULL,
- ARG_NO_RELOAD,
- ARG_KILL_MODE,
- ARG_KILL_WHO,
- ARG_NO_ASK_PASSWORD,
- ARG_FAILED,
- ARG_RUNTIME,
- ARG_FOLLOW,
- ARG_FORCE
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "type", required_argument, NULL, 't' },
- { "property", required_argument, NULL, 'p' },
- { "all", no_argument, NULL, 'a' },
- { "failed", no_argument, NULL, ARG_FAILED },
- { "full", no_argument, NULL, ARG_FULL },
- { "fail", no_argument, NULL, ARG_FAIL },
- { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
- { "user", no_argument, NULL, ARG_USER },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "global", no_argument, NULL, ARG_GLOBAL },
- { "no-block", no_argument, NULL, ARG_NO_BLOCK },
- { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "no-wall", no_argument, NULL, ARG_NO_WALL },
- { "quiet", no_argument, NULL, 'q' },
- { "order", no_argument, NULL, ARG_ORDER },
- { "require", no_argument, NULL, ARG_REQUIRE },
- { "root", required_argument, NULL, ARG_ROOT },
- { "force", no_argument, NULL, ARG_FORCE },
- { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
- { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
- { "kill-who", required_argument, NULL, ARG_KILL_WHO },
- { "signal", required_argument, NULL, 's' },
- { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
- { "host", required_argument, NULL, 'H' },
- { "privileged",no_argument, NULL, 'P' },
- { "runtime", no_argument, NULL, ARG_RUNTIME },
- { "lines", required_argument, NULL, 'n' },
- { "follow", no_argument, NULL, ARG_FOLLOW },
- { "output", required_argument, NULL, 'o' },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- systemctl_help();
- return 0;
-
- case ARG_VERSION:
- puts(PACKAGE_STRING);
- puts(DISTRIBUTION);
- puts(SYSTEMD_FEATURES);
- return 0;
-
- case 't':
- arg_type = optarg;
- break;
-
- case 'p': {
- char **l;
-
- if (!(l = strv_append(arg_property, optarg)))
- return -ENOMEM;
-
- strv_free(arg_property);
- arg_property = l;
-
- /* If the user asked for a particular
- * property, show it to him, even if it is
- * empty. */
- arg_all = true;
- break;
- }
-
- case 'a':
- arg_all = true;
- break;
-
- case ARG_FAIL:
- arg_job_mode = "fail";
- break;
-
- case ARG_IGNORE_DEPENDENCIES:
- arg_job_mode = "ignore-dependencies";
- break;
-
- case ARG_USER:
- arg_scope = UNIT_FILE_USER;
- break;
-
- case ARG_SYSTEM:
- arg_scope = UNIT_FILE_SYSTEM;
- break;
-
- case ARG_GLOBAL:
- arg_scope = UNIT_FILE_GLOBAL;
- break;
-
- case ARG_NO_BLOCK:
- arg_no_block = true;
- break;
-
- case ARG_NO_LEGEND:
- arg_no_legend = true;
- break;
-
- case ARG_NO_PAGER:
- arg_no_pager = true;
- break;
-
- case ARG_NO_WALL:
- arg_no_wall = true;
- break;
-
- case ARG_ORDER:
- arg_dot = DOT_ORDER;
- break;
-
- case ARG_REQUIRE:
- arg_dot = DOT_REQUIRE;
- break;
-
- case ARG_ROOT:
- arg_root = optarg;
- break;
-
- case ARG_FULL:
- arg_full = true;
- break;
-
- case ARG_FAILED:
- arg_failed = true;
- break;
-
- case 'q':
- arg_quiet = true;
- break;
-
- case ARG_FORCE:
- arg_force ++;
- break;
-
- case ARG_FOLLOW:
- arg_follow = true;
- break;
-
- case 'f':
- /* -f is short for both --follow and --force! */
- arg_force ++;
- arg_follow = true;
- break;
-
- case ARG_NO_RELOAD:
- arg_no_reload = true;
- break;
-
- case ARG_KILL_WHO:
- arg_kill_who = optarg;
- break;
-
- case ARG_KILL_MODE:
- arg_kill_mode = optarg;
- break;
-
- case 's':
- if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
- log_error("Failed to parse signal string %s.", optarg);
- return -EINVAL;
- }
- break;
-
- case ARG_NO_ASK_PASSWORD:
- arg_ask_password = false;
- break;
-
- case 'P':
- arg_transport = TRANSPORT_POLKIT;
- break;
-
- case 'H':
- arg_transport = TRANSPORT_SSH;
- arg_host = optarg;
- break;
-
- case ARG_RUNTIME:
- arg_runtime = true;
- break;
-
- case 'n':
- if (safe_atou(optarg, &arg_lines) < 0) {
- log_error("Failed to parse lines '%s'", optarg);
- return -EINVAL;
- }
- break;
-
- case 'o':
- arg_output = output_mode_from_string(optarg);
- if (arg_output < 0) {
- log_error("Unknown output '%s'.", optarg);
- return -EINVAL;
- }
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
- log_error("Cannot access user instance remotely.");
- return -EINVAL;
- }
-
- return 1;
-}
-
-static int halt_parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_HELP = 0x100,
- ARG_HALT,
- ARG_REBOOT,
- ARG_NO_WALL
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, ARG_HELP },
- { "halt", no_argument, NULL, ARG_HALT },
- { "poweroff", no_argument, NULL, 'p' },
- { "reboot", no_argument, NULL, ARG_REBOOT },
- { "force", no_argument, NULL, 'f' },
- { "wtmp-only", no_argument, NULL, 'w' },
- { "no-wtmp", no_argument, NULL, 'd' },
- { "no-sync", no_argument, NULL, 'n' },
- { "no-wall", no_argument, NULL, ARG_NO_WALL },
- { NULL, 0, NULL, 0 }
- };
-
- int c, runlevel;
-
- assert(argc >= 0);
- assert(argv);
-
- if (utmp_get_runlevel(&runlevel, NULL) >= 0)
- if (runlevel == '0' || runlevel == '6')
- arg_immediate = true;
-
- while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
- switch (c) {
-
- case ARG_HELP:
- halt_help();
- return 0;
-
- case ARG_HALT:
- arg_action = ACTION_HALT;
- break;
-
- case 'p':
- if (arg_action != ACTION_REBOOT)
- arg_action = ACTION_POWEROFF;
- break;
-
- case ARG_REBOOT:
- arg_action = ACTION_REBOOT;
- break;
-
- case 'f':
- arg_immediate = true;
- break;
-
- case 'w':
- arg_dry = true;
- break;
-
- case 'd':
- arg_no_wtmp = true;
- break;
-
- case 'n':
- arg_no_sync = true;
- break;
-
- case ARG_NO_WALL:
- arg_no_wall = true;
- break;
-
- case 'i':
- case 'h':
- /* Compatibility nops */
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- return 1;
-}
-
-static int parse_time_spec(const char *t, usec_t *_u) {
- assert(t);
- assert(_u);
-
- if (streq(t, "now"))
- *_u = 0;
- else if (!strchr(t, ':')) {
- uint64_t u;
-
- if (safe_atou64(t, &u) < 0)
- return -EINVAL;
-
- *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
- } else {
- char *e = NULL;
- long hour, minute;
- struct tm tm;
- time_t s;
- usec_t n;
-
- errno = 0;
- hour = strtol(t, &e, 10);
- if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
- return -EINVAL;
-
- minute = strtol(e+1, &e, 10);
- if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
- return -EINVAL;
-
- n = now(CLOCK_REALTIME);
- s = (time_t) (n / USEC_PER_SEC);
-
- zero(tm);
- assert_se(localtime_r(&s, &tm));
-
- tm.tm_hour = (int) hour;
- tm.tm_min = (int) minute;
- tm.tm_sec = 0;
-
- assert_se(s = mktime(&tm));
-
- *_u = (usec_t) s * USEC_PER_SEC;
-
- while (*_u <= n)
- *_u += USEC_PER_DAY;
- }
-
- return 0;
-}
-
-static int shutdown_parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_HELP = 0x100,
- ARG_NO_WALL
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, ARG_HELP },
- { "halt", no_argument, NULL, 'H' },
- { "poweroff", no_argument, NULL, 'P' },
- { "reboot", no_argument, NULL, 'r' },
- { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
- { "no-wall", no_argument, NULL, ARG_NO_WALL },
- { NULL, 0, NULL, 0 }
- };
-
- int c, r;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
- switch (c) {
-
- case ARG_HELP:
- shutdown_help();
- return 0;
-
- case 'H':
- arg_action = ACTION_HALT;
- break;
-
- case 'P':
- arg_action = ACTION_POWEROFF;
- break;
-
- case 'r':
- if (kexec_loaded())
- arg_action = ACTION_KEXEC;
- else
- arg_action = ACTION_REBOOT;
- break;
-
- case 'K':
- arg_action = ACTION_KEXEC;
- break;
-
- case 'h':
- if (arg_action != ACTION_HALT)
- arg_action = ACTION_POWEROFF;
- break;
-
- case 'k':
- arg_dry = true;
- break;
-
- case ARG_NO_WALL:
- arg_no_wall = true;
- break;
-
- case 't':
- case 'a':
- /* Compatibility nops */
- break;
-
- case 'c':
- arg_action = ACTION_CANCEL_SHUTDOWN;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (argc > optind) {
- r = parse_time_spec(argv[optind], &arg_when);
- if (r < 0) {
- log_error("Failed to parse time specification: %s", argv[optind]);
- return r;
- }
- } else
- arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
-
- /* We skip the time argument */
- if (argc > optind + 1)
- arg_wall = argv + optind + 1;
-
- optind = argc;
-
- return 1;
-}
-
-static int telinit_parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_HELP = 0x100,
- ARG_NO_WALL
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, ARG_HELP },
- { "no-wall", no_argument, NULL, ARG_NO_WALL },
- { NULL, 0, NULL, 0 }
- };
-
- static const struct {
- char from;
- enum action to;
- } table[] = {
- { '0', ACTION_POWEROFF },
- { '6', ACTION_REBOOT },
- { '1', ACTION_RESCUE },
- { '2', ACTION_RUNLEVEL2 },
- { '3', ACTION_RUNLEVEL3 },
- { '4', ACTION_RUNLEVEL4 },
- { '5', ACTION_RUNLEVEL5 },
- { 's', ACTION_RESCUE },
- { 'S', ACTION_RESCUE },
- { 'q', ACTION_RELOAD },
- { 'Q', ACTION_RELOAD },
- { 'u', ACTION_REEXEC },
- { 'U', ACTION_REEXEC }
- };
-
- unsigned i;
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
- switch (c) {
-
- case ARG_HELP:
- telinit_help();
- return 0;
-
- case ARG_NO_WALL:
- arg_no_wall = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind >= argc) {
- telinit_help();
- return -EINVAL;
- }
-
- if (optind + 1 < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- if (strlen(argv[optind]) != 1) {
- log_error("Expected single character argument.");
- return -EINVAL;
- }
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (table[i].from == argv[optind][0])
- break;
-
- if (i >= ELEMENTSOF(table)) {
- log_error("Unknown command %s.", argv[optind]);
- return -EINVAL;
- }
-
- arg_action = table[i].to;
-
- optind ++;
-
- return 1;
-}
-
-static int runlevel_parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_HELP = 0x100,
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, ARG_HELP },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
- switch (c) {
-
- case ARG_HELP:
- runlevel_help();
- return 0;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind < argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- return 1;
-}
-
-static int parse_argv(int argc, char *argv[]) {
- assert(argc >= 0);
- assert(argv);
-
- if (program_invocation_short_name) {
-
- if (strstr(program_invocation_short_name, "halt")) {
- arg_action = ACTION_HALT;
- return halt_parse_argv(argc, argv);
- } else if (strstr(program_invocation_short_name, "poweroff")) {
- arg_action = ACTION_POWEROFF;
- return halt_parse_argv(argc, argv);
- } else if (strstr(program_invocation_short_name, "reboot")) {
- if (kexec_loaded())
- arg_action = ACTION_KEXEC;
- else
- arg_action = ACTION_REBOOT;
- return halt_parse_argv(argc, argv);
- } else if (strstr(program_invocation_short_name, "shutdown")) {
- arg_action = ACTION_POWEROFF;
- return shutdown_parse_argv(argc, argv);
- } else if (strstr(program_invocation_short_name, "init")) {
-
- if (sd_booted() > 0) {
- arg_action = ACTION_INVALID;
- return telinit_parse_argv(argc, argv);
- } else {
- /* Hmm, so some other init system is
- * running, we need to forward this
- * request to it. For now we simply
- * guess that it is Upstart. */
-
- execv("/lib/upstart/telinit", argv);
-
- log_error("Couldn't find an alternative telinit implementation to spawn.");
- return -EIO;
- }
-
- } else if (strstr(program_invocation_short_name, "runlevel")) {
- arg_action = ACTION_RUNLEVEL;
- return runlevel_parse_argv(argc, argv);
- }
- }
-
- arg_action = ACTION_SYSTEMCTL;
- return systemctl_parse_argv(argc, argv);
-}
-
-static int action_to_runlevel(void) {
-
- static const char table[_ACTION_MAX] = {
- [ACTION_HALT] = '0',
- [ACTION_POWEROFF] = '0',
- [ACTION_REBOOT] = '6',
- [ACTION_RUNLEVEL2] = '2',
- [ACTION_RUNLEVEL3] = '3',
- [ACTION_RUNLEVEL4] = '4',
- [ACTION_RUNLEVEL5] = '5',
- [ACTION_RESCUE] = '1'
- };
-
- assert(arg_action < _ACTION_MAX);
-
- return table[arg_action];
-}
-
-static int talk_upstart(void) {
- DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- int previous, rl, r;
- char
- env1_buf[] = "RUNLEVEL=X",
- env2_buf[] = "PREVLEVEL=X";
- char *env1 = env1_buf, *env2 = env2_buf;
- const char *emit = "runlevel";
- dbus_bool_t b_false = FALSE;
- DBusMessageIter iter, sub;
- DBusConnection *bus;
-
- dbus_error_init(&error);
-
- if (!(rl = action_to_runlevel()))
- return 0;
-
- if (utmp_get_runlevel(&previous, NULL) < 0)
- previous = 'N';
-
- if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
- if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
- r = 0;
- goto finish;
- }
-
- log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if ((r = bus_check_peercred(bus)) < 0) {
- log_error("Failed to verify owner of bus.");
- goto finish;
- }
-
- if (!(m = dbus_message_new_method_call(
- "com.ubuntu.Upstart",
- "/com/ubuntu/Upstart",
- "com.ubuntu.Upstart0_6",
- "EmitEvent"))) {
-
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_iter_init_append(m, &iter);
-
- env1_buf[sizeof(env1_buf)-2] = rl;
- env2_buf[sizeof(env2_buf)-2] = previous;
-
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
- !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
- !dbus_message_iter_close_container(&iter, &sub) ||
- !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
- if (error_is_no_service(&error)) {
- r = -EADDRNOTAVAIL;
- goto finish;
- }
-
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- r = 1;
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- if (bus) {
- dbus_connection_flush(bus);
- dbus_connection_close(bus);
- dbus_connection_unref(bus);
- }
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int talk_initctl(void) {
- struct init_request request;
- int r, fd;
- char rl;
-
- if (!(rl = action_to_runlevel()))
- return 0;
-
- zero(request);
- request.magic = INIT_MAGIC;
- request.sleeptime = 0;
- request.cmd = INIT_CMD_RUNLVL;
- request.runlevel = rl;
-
- if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
-
- if (errno == ENOENT)
- return 0;
-
- log_error("Failed to open "INIT_FIFO": %m");
- return -errno;
- }
-
- errno = 0;
- r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
- close_nointr_nofail(fd);
-
- if (r < 0) {
- log_error("Failed to write to "INIT_FIFO": %m");
- return errno ? -errno : -EIO;
- }
-
- return 1;
-}
-
-static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
-
- static const struct {
- const char* verb;
- const enum {
- MORE,
- LESS,
- EQUAL
- } argc_cmp;
- const int argc;
- int (* const dispatch)(DBusConnection *bus, char **args);
- } verbs[] = {
- { "list-units", LESS, 1, list_units },
- { "list-unit-files", EQUAL, 1, list_unit_files },
- { "list-jobs", EQUAL, 1, list_jobs },
- { "clear-jobs", EQUAL, 1, daemon_reload },
- { "load", MORE, 2, load_unit },
- { "cancel", MORE, 2, cancel_job },
- { "start", MORE, 2, start_unit },
- { "stop", MORE, 2, start_unit },
- { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
- { "reload", MORE, 2, start_unit },
- { "restart", MORE, 2, start_unit },
- { "try-restart", MORE, 2, start_unit },
- { "reload-or-restart", MORE, 2, start_unit },
- { "reload-or-try-restart", MORE, 2, start_unit },
- { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
- { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
- { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
- { "isolate", EQUAL, 2, start_unit },
- { "kill", MORE, 2, kill_unit },
- { "is-active", MORE, 2, check_unit },
- { "check", MORE, 2, check_unit },
- { "show", MORE, 1, show },
- { "status", MORE, 2, show },
- { "dump", EQUAL, 1, dump },
- { "dot", EQUAL, 1, dot },
- { "snapshot", LESS, 2, snapshot },
- { "delete", MORE, 2, delete_snapshot },
- { "daemon-reload", EQUAL, 1, daemon_reload },
- { "daemon-reexec", EQUAL, 1, daemon_reload },
- { "show-environment", EQUAL, 1, show_enviroment },
- { "set-environment", MORE, 2, set_environment },
- { "unset-environment", MORE, 2, set_environment },
- { "halt", EQUAL, 1, start_special },
- { "poweroff", EQUAL, 1, start_special },
- { "reboot", EQUAL, 1, start_special },
- { "kexec", EQUAL, 1, start_special },
- { "default", EQUAL, 1, start_special },
- { "rescue", EQUAL, 1, start_special },
- { "emergency", EQUAL, 1, start_special },
- { "exit", EQUAL, 1, start_special },
- { "reset-failed", MORE, 1, reset_failed },
- { "enable", MORE, 2, enable_unit },
- { "disable", MORE, 2, enable_unit },
- { "is-enabled", MORE, 2, unit_is_enabled },
- { "reenable", MORE, 2, enable_unit },
- { "preset", MORE, 2, enable_unit },
- { "mask", MORE, 2, enable_unit },
- { "unmask", MORE, 2, enable_unit },
- { "link", MORE, 2, enable_unit }
- };
-
- int left;
- unsigned i;
-
- assert(argc >= 0);
- assert(argv);
- assert(error);
-
- left = argc - optind;
-
- if (left <= 0)
- /* Special rule: no arguments means "list-units" */
- i = 0;
- else {
- if (streq(argv[optind], "help")) {
- systemctl_help();
- return 0;
- }
-
- for (i = 0; i < ELEMENTSOF(verbs); i++)
- if (streq(argv[optind], verbs[i].verb))
- break;
-
- if (i >= ELEMENTSOF(verbs)) {
- log_error("Unknown operation %s", argv[optind]);
- return -EINVAL;
- }
- }
-
- switch (verbs[i].argc_cmp) {
-
- case EQUAL:
- if (left != verbs[i].argc) {
- log_error("Invalid number of arguments.");
- return -EINVAL;
- }
-
- break;
-
- case MORE:
- if (left < verbs[i].argc) {
- log_error("Too few arguments.");
- return -EINVAL;
- }
-
- break;
-
- case LESS:
- if (left > verbs[i].argc) {
- log_error("Too many arguments.");
- return -EINVAL;
- }
-
- break;
-
- default:
- assert_not_reached("Unknown comparison operator.");
- }
-
- /* Require a bus connection for all operations but
- * enable/disable */
- if (!streq(verbs[i].verb, "enable") &&
- !streq(verbs[i].verb, "disable") &&
- !streq(verbs[i].verb, "is-enabled") &&
- !streq(verbs[i].verb, "list-unit-files") &&
- !streq(verbs[i].verb, "reenable") &&
- !streq(verbs[i].verb, "preset") &&
- !streq(verbs[i].verb, "mask") &&
- !streq(verbs[i].verb, "unmask") &&
- !streq(verbs[i].verb, "link")) {
-
- if (running_in_chroot() > 0) {
- log_info("Running in chroot, ignoring request.");
- return 0;
- }
-
- if (!bus) {
- log_error("Failed to get D-Bus connection: %s",
- dbus_error_is_set(error) ? error->message : "No connection to service manager.");
- return -EIO;
- }
-
- } else {
-
- if (!bus && !avoid_bus()) {
- log_error("Failed to get D-Bus connection: %s",
- dbus_error_is_set(error) ? error->message : "No connection to service manager.");
- return -EIO;
- }
- }
-
- return verbs[i].dispatch(bus, argv + optind);
-}
-
-static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
- int fd;
- struct msghdr msghdr;
- struct iovec iovec[2];
- union sockaddr_union sockaddr;
- struct sd_shutdown_command c;
-
- fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (fd < 0)
- return -errno;
-
- zero(c);
- c.usec = t;
- c.mode = mode;
- c.dry_run = dry_run;
- c.warn_wall = warn;
-
- zero(sockaddr);
- sockaddr.sa.sa_family = AF_UNIX;
- strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
-
- zero(msghdr);
- msghdr.msg_name = &sockaddr;
- msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
-
- zero(iovec);
- iovec[0].iov_base = (char*) &c;
- iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
-
- if (isempty(message))
- msghdr.msg_iovlen = 1;
- else {
- iovec[1].iov_base = (char*) message;
- iovec[1].iov_len = strlen(message);
- msghdr.msg_iovlen = 2;
- }
- msghdr.msg_iov = iovec;
-
- if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- close_nointr_nofail(fd);
- return 0;
-}
-
-static int reload_with_fallback(DBusConnection *bus) {
-
- if (bus) {
- /* First, try systemd via D-Bus. */
- if (daemon_reload(bus, NULL) >= 0)
- return 0;
- }
-
- /* Nothing else worked, so let's try signals */
- assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
-
- if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
- log_error("kill() failed: %m");
- return -errno;
- }
-
- return 0;
-}
-
-static int start_with_fallback(DBusConnection *bus) {
-
- if (bus) {
- /* First, try systemd via D-Bus. */
- if (start_unit(bus, NULL) >= 0)
- goto done;
- }
-
- /* Hmm, talking to systemd via D-Bus didn't work. Then
- * let's try to talk to Upstart via D-Bus. */
- if (talk_upstart() > 0)
- goto done;
-
- /* Nothing else worked, so let's try
- * /dev/initctl */
- if (talk_initctl() > 0)
- goto done;
-
- log_error("Failed to talk to init daemon.");
- return -EIO;
-
-done:
- warn_wall(arg_action);
- return 0;
-}
-
-static void halt_now(enum action a) {
-
- /* Make sure C-A-D is handled by the kernel from this
- * point on... */
- reboot(RB_ENABLE_CAD);
-
- switch (a) {
-
- case ACTION_HALT:
- log_info("Halting.");
- reboot(RB_HALT_SYSTEM);
- break;
-
- case ACTION_POWEROFF:
- log_info("Powering off.");
- reboot(RB_POWER_OFF);
- break;
-
- case ACTION_REBOOT:
- log_info("Rebooting.");
- reboot(RB_AUTOBOOT);
- break;
-
- default:
- assert_not_reached("Unknown halt action.");
- }
-
- assert_not_reached("Uh? This shouldn't happen.");
-}
-
-static int halt_main(DBusConnection *bus) {
- int r;
-
- if (geteuid() != 0) {
- /* Try logind if we are a normal user and no special
- * mode applies. Maybe PolicyKit allows us to shutdown
- * the machine. */
-
- if (arg_when <= 0 &&
- !arg_dry &&
- !arg_immediate &&
- (arg_action == ACTION_POWEROFF ||
- arg_action == ACTION_REBOOT)) {
- r = reboot_with_logind(bus, arg_action);
- if (r >= 0)
- return r;
- }
-
- log_error("Must be root.");
- return -EPERM;
- }
-
- if (arg_when > 0) {
- char *m;
-
- m = strv_join(arg_wall, " ");
- r = send_shutdownd(arg_when,
- arg_action == ACTION_HALT ? 'H' :
- arg_action == ACTION_POWEROFF ? 'P' :
- arg_action == ACTION_KEXEC ? 'K' :
- 'r',
- arg_dry,
- !arg_no_wall,
- m);
- free(m);
-
- if (r < 0)
- log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
- else {
- char date[FORMAT_TIMESTAMP_MAX];
-
- log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
- format_timestamp(date, sizeof(date), arg_when));
- return 0;
- }
- }
-
- if (!arg_dry && !arg_immediate)
- return start_with_fallback(bus);
-
- if (!arg_no_wtmp) {
- if (sd_booted() > 0)
- log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
- else {
- r = utmp_put_shutdown();
- if (r < 0)
- log_warning("Failed to write utmp record: %s", strerror(-r));
- }
- }
-
- if (!arg_no_sync)
- sync();
-
- if (arg_dry)
- return 0;
-
- halt_now(arg_action);
- /* We should never reach this. */
- return -ENOSYS;
-}
-
-static int runlevel_main(void) {
- int r, runlevel, previous;
-
- r = utmp_get_runlevel(&runlevel, &previous);
- if (r < 0) {
- puts("unknown");
- return r;
- }
-
- printf("%c %c\n",
- previous <= 0 ? 'N' : previous,
- runlevel <= 0 ? 'N' : runlevel);
-
- return 0;
-}
-
-int main(int argc, char*argv[]) {
- int r, retval = EXIT_FAILURE;
- DBusConnection *bus = NULL;
- DBusError error;
-
- dbus_error_init(&error);
-
- log_parse_environment();
- log_open();
-
- r = parse_argv(argc, argv);
- if (r < 0)
- goto finish;
- else if (r == 0) {
- retval = EXIT_SUCCESS;
- goto finish;
- }
-
- /* /sbin/runlevel doesn't need to communicate via D-Bus, so
- * let's shortcut this */
- if (arg_action == ACTION_RUNLEVEL) {
- r = runlevel_main();
- retval = r < 0 ? EXIT_FAILURE : r;
- goto finish;
- }
-
- if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
- log_info("Running in chroot, ignoring request.");
- retval = 0;
- goto finish;
- }
-
- if (!avoid_bus()) {
- if (arg_transport == TRANSPORT_NORMAL)
- bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
- else if (arg_transport == TRANSPORT_POLKIT) {
- bus_connect_system_polkit(&bus, &error);
- private_bus = false;
- } else if (arg_transport == TRANSPORT_SSH) {
- bus_connect_system_ssh(NULL, arg_host, &bus, &error);
- private_bus = false;
- } else
- assert_not_reached("Uh, invalid transport...");
- }
-
- switch (arg_action) {
-
- case ACTION_SYSTEMCTL:
- r = systemctl_main(bus, argc, argv, &error);
- break;
-
- case ACTION_HALT:
- case ACTION_POWEROFF:
- case ACTION_REBOOT:
- case ACTION_KEXEC:
- r = halt_main(bus);
- break;
-
- case ACTION_RUNLEVEL2:
- case ACTION_RUNLEVEL3:
- case ACTION_RUNLEVEL4:
- case ACTION_RUNLEVEL5:
- case ACTION_RESCUE:
- case ACTION_EMERGENCY:
- case ACTION_DEFAULT:
- r = start_with_fallback(bus);
- break;
-
- case ACTION_RELOAD:
- case ACTION_REEXEC:
- r = reload_with_fallback(bus);
- break;
-
- case ACTION_CANCEL_SHUTDOWN:
- r = send_shutdownd(0, 0, false, false, NULL);
- break;
-
- case ACTION_INVALID:
- case ACTION_RUNLEVEL:
- default:
- assert_not_reached("Unknown action");
- }
-
- retval = r < 0 ? EXIT_FAILURE : r;
-
-finish:
- if (bus) {
- dbus_connection_flush(bus);
- dbus_connection_close(bus);
- dbus_connection_unref(bus);
- }
-
- dbus_error_free(&error);
-
- dbus_shutdown();
-
- strv_free(arg_property);
-
- pager_close();
- ask_password_agent_close();
- polkit_agent_close();
-
- return retval;
-}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
new file mode 100644
index 0000000..28bdfa9
--- /dev/null
+++ b/src/systemctl/systemctl.c
@@ -0,0 +1,5523 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/reboot.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <dbus/dbus.h>
+
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+#include "set.h"
+#include "utmp-wtmp.h"
+#include "special.h"
+#include "initreq.h"
+#include "strv.h"
+#include "dbus-common.h"
+#include "cgroup-show.h"
+#include "cgroup-util.h"
+#include "list.h"
+#include "path-lookup.h"
+#include "conf-parser.h"
+#include "exit-status.h"
+#include "bus-errors.h"
+#include "build.h"
+#include "unit-name.h"
+#include "pager.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
+#include "install.h"
+#include "logs-show.h"
+
+static const char *arg_type = NULL;
+static char **arg_property = NULL;
+static bool arg_all = false;
+static const char *arg_job_mode = "replace";
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static bool arg_immediate = false;
+static bool arg_no_block = false;
+static bool arg_no_legend = false;
+static bool arg_no_pager = false;
+static bool arg_no_wtmp = false;
+static bool arg_no_sync = false;
+static bool arg_no_wall = false;
+static bool arg_no_reload = false;
+static bool arg_dry = false;
+static bool arg_quiet = false;
+static bool arg_full = false;
+static int arg_force = 0;
+static bool arg_ask_password = true;
+static bool arg_failed = false;
+static bool arg_runtime = false;
+static char **arg_wall = NULL;
+static const char *arg_kill_who = NULL;
+static const char *arg_kill_mode = NULL;
+static int arg_signal = SIGTERM;
+static const char *arg_root = NULL;
+static usec_t arg_when = 0;
+static enum action {
+ ACTION_INVALID,
+ ACTION_SYSTEMCTL,
+ ACTION_HALT,
+ ACTION_POWEROFF,
+ ACTION_REBOOT,
+ ACTION_KEXEC,
+ ACTION_EXIT,
+ ACTION_RUNLEVEL2,
+ ACTION_RUNLEVEL3,
+ ACTION_RUNLEVEL4,
+ ACTION_RUNLEVEL5,
+ ACTION_RESCUE,
+ ACTION_EMERGENCY,
+ ACTION_DEFAULT,
+ ACTION_RELOAD,
+ ACTION_REEXEC,
+ ACTION_RUNLEVEL,
+ ACTION_CANCEL_SHUTDOWN,
+ _ACTION_MAX
+} arg_action = ACTION_SYSTEMCTL;
+static enum dot {
+ DOT_ALL,
+ DOT_ORDER,
+ DOT_REQUIRE
+} arg_dot = DOT_ALL;
+static enum transport {
+ TRANSPORT_NORMAL,
+ TRANSPORT_SSH,
+ TRANSPORT_POLKIT
+} arg_transport = TRANSPORT_NORMAL;
+static const char *arg_host = NULL;
+static bool arg_follow = false;
+static unsigned arg_lines = 10;
+static OutputMode arg_output = OUTPUT_SHORT;
+
+static bool private_bus = false;
+
+static int daemon_reload(DBusConnection *bus, char **args);
+static void halt_now(enum action a);
+
+static bool on_tty(void) {
+ static int t = -1;
+
+ /* Note that this is invoked relatively early, before we start
+ * the pager. That means the value we return reflects whether
+ * we originally were started on a tty, not if we currently
+ * are. But this is intended, since we want colour and so on
+ * when run in our own pager. */
+
+ if (_unlikely_(t < 0))
+ t = isatty(STDOUT_FILENO) > 0;
+
+ return t;
+}
+
+static void pager_open_if_enabled(void) {
+
+ /* Cache result before we open the pager */
+ on_tty();
+
+ if (arg_no_pager)
+ return;
+
+ pager_open();
+}
+
+static void ask_password_agent_open_if_enabled(void) {
+
+ /* Open the password agent as a child process if necessary */
+
+ if (!arg_ask_password)
+ return;
+
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ return;
+
+ ask_password_agent_open();
+}
+
+static void polkit_agent_open_if_enabled(void) {
+
+ /* Open the polkit agent as a child process if necessary */
+
+ if (!arg_ask_password)
+ return;
+
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ return;
+
+ polkit_agent_open();
+}
+
+static const char *ansi_highlight_red(bool b) {
+
+ if (!on_tty())
+ return "";
+
+ return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
+}
+
+static const char *ansi_highlight_green(bool b) {
+
+ if (!on_tty())
+ return "";
+
+ return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
+}
+
+static bool error_is_no_service(const DBusError *error) {
+ assert(error);
+
+ if (!dbus_error_is_set(error))
+ return false;
+
+ if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
+ return true;
+
+ if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
+ return true;
+
+ return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
+}
+
+static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
+ assert(error);
+
+ if (!dbus_error_is_set(error))
+ return r;
+
+ if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
+ dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
+ dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
+ dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
+ return EXIT_NOPERMISSION;
+
+ if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
+ return EXIT_NOTINSTALLED;
+
+ if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
+ dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
+ return EXIT_NOTIMPLEMENTED;
+
+ if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
+ return EXIT_NOTCONFIGURED;
+
+ if (r != 0)
+ return r;
+
+ return EXIT_FAILURE;
+}
+
+static void warn_wall(enum action a) {
+ static const char *table[_ACTION_MAX] = {
+ [ACTION_HALT] = "The system is going down for system halt NOW!",
+ [ACTION_REBOOT] = "The system is going down for reboot NOW!",
+ [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
+ [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!",
+ [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
+ [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
+ };
+
+ if (arg_no_wall)
+ return;
+
+ if (arg_wall) {
+ char *p;
+
+ p = strv_join(arg_wall, " ");
+ if (!p) {
+ log_error("Failed to join strings.");
+ return;
+ }
+
+ if (*p) {
+ utmp_wall(p, NULL);
+ free(p);
+ return;
+ }
+
+ free(p);
+ }
+
+ if (!table[a])
+ return;
+
+ utmp_wall(table[a], NULL);
+}
+
+static bool avoid_bus(void) {
+
+ if (running_in_chroot() > 0)
+ return true;
+
+ if (sd_booted() <= 0)
+ return true;
+
+ if (!isempty(arg_root))
+ return true;
+
+ if (arg_scope == UNIT_FILE_GLOBAL)
+ return true;
+
+ return false;
+}
+
+struct unit_info {
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+};
+
+static int compare_unit_info(const void *a, const void *b) {
+ const char *d1, *d2;
+ const struct unit_info *u = a, *v = b;
+
+ d1 = strrchr(u->id, '.');
+ d2 = strrchr(v->id, '.');
+
+ if (d1 && d2) {
+ int r;
+
+ if ((r = strcasecmp(d1, d2)) != 0)
+ return r;
+ }
+
+ return strcasecmp(u->id, v->id);
+}
+
+static bool output_show_unit(const struct unit_info *u) {
+ const char *dot;
+
+ if (arg_failed)
+ return streq(u->active_state, "failed");
+
+ return (!arg_type || ((dot = strrchr(u->id, '.')) &&
+ streq(dot+1, arg_type))) &&
+ (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
+}
+
+static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
+ unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
+ const struct unit_info *u;
+
+ max_id_len = sizeof("UNIT")-1;
+ active_len = sizeof("ACTIVE")-1;
+ sub_len = sizeof("SUB")-1;
+ job_len = sizeof("JOB")-1;
+ desc_len = 0;
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ if (!output_show_unit(u))
+ continue;
+
+ max_id_len = MAX(max_id_len, strlen(u->id));
+ active_len = MAX(active_len, strlen(u->active_state));
+ sub_len = MAX(sub_len, strlen(u->sub_state));
+ if (u->job_id != 0)
+ job_len = MAX(job_len, strlen(u->job_type));
+ }
+
+ if (!arg_full) {
+ unsigned basic_len;
+ id_len = MIN(max_id_len, 25);
+ basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
+ if (basic_len < (unsigned) columns()) {
+ unsigned extra_len, incr;
+ extra_len = columns() - basic_len;
+ /* Either UNIT already got 25, or is fully satisfied.
+ * Grant up to 25 to DESC now. */
+ incr = MIN(extra_len, 25);
+ desc_len += incr;
+ extra_len -= incr;
+ /* split the remaining space between UNIT and DESC,
+ * but do not give UNIT more than it needs. */
+ if (extra_len > 0) {
+ incr = MIN(extra_len / 2, max_id_len - id_len);
+ id_len += incr;
+ desc_len += extra_len - incr;
+ }
+ }
+ } else
+ id_len = max_id_len;
+
+ if (!arg_no_legend) {
+ printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
+ active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
+ if (!arg_full && arg_no_pager)
+ printf("%.*s\n", desc_len, "DESCRIPTION");
+ else
+ printf("%s\n", "DESCRIPTION");
+ }
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ char *e;
+ const char *on_loaded, *off_loaded;
+ const char *on_active, *off_active;
+
+ if (!output_show_unit(u))
+ continue;
+
+ n_shown++;
+
+ if (streq(u->load_state, "error")) {
+ on_loaded = ansi_highlight_red(true);
+ off_loaded = ansi_highlight_red(false);
+ } else
+ on_loaded = off_loaded = "";
+
+ if (streq(u->active_state, "failed")) {
+ on_active = ansi_highlight_red(true);
+ off_active = ansi_highlight_red(false);
+ } else
+ on_active = off_active = "";
+
+ e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
+
+ printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
+ id_len, e ? e : u->id,
+ on_loaded, u->load_state, off_loaded,
+ on_active, active_len, u->active_state,
+ sub_len, u->sub_state, off_active,
+ job_len, u->job_id ? u->job_type : "");
+ if (!arg_full && arg_no_pager)
+ printf("%.*s\n", desc_len, u->description);
+ else
+ printf("%s\n", u->description);
+
+ free(e);
+ }
+
+ if (!arg_no_legend) {
+ printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n"
+ "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
+ "SUB = The low-level unit activation state, values depend on unit type.\n"
+ "JOB = Pending job for the unit.\n");
+
+ if (arg_all)
+ printf("\n%u units listed.\n", n_shown);
+ else
+ printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
+ }
+}
+
+static int list_units(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ DBusMessageIter iter, sub, sub2;
+ unsigned c = 0, n_units = 0;
+ struct unit_info *unit_infos = NULL;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+
+ pager_open_if_enabled();
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ struct unit_info *u;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (c >= n_units) {
+ struct unit_info *w;
+
+ n_units = MAX(2*c, 16);
+ w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
+
+ if (!w) {
+ log_error("Failed to allocate unit array.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ unit_infos = w;
+ }
+
+ u = unit_infos+c;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_next(&sub);
+ c++;
+ }
+
+ if (c > 0) {
+ qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+ output_units_list(unit_infos, c);
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ free(unit_infos);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int compare_unit_file_list(const void *a, const void *b) {
+ const char *d1, *d2;
+ const UnitFileList *u = a, *v = b;
+
+ d1 = strrchr(u->path, '.');
+ d2 = strrchr(v->path, '.');
+
+ if (d1 && d2) {
+ int r;
+
+ r = strcasecmp(d1, d2);
+ if (r != 0)
+ return r;
+ }
+
+ return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
+}
+
+static bool output_show_unit_file(const UnitFileList *u) {
+ const char *dot;
+
+ return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+}
+
+static void output_unit_file_list(const UnitFileList *units, unsigned c) {
+ unsigned max_id_len, id_cols, state_cols, n_shown = 0;
+ const UnitFileList *u;
+
+ max_id_len = sizeof("UNIT FILE")-1;
+ state_cols = sizeof("STATE")-1;
+ for (u = units; u < units + c; u++) {
+ if (!output_show_unit_file(u))
+ continue;
+
+ max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
+ state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
+ }
+
+ if (!arg_full) {
+ unsigned basic_cols;
+ id_cols = MIN(max_id_len, 25);
+ basic_cols = 1 + id_cols + state_cols;
+ if (basic_cols < (unsigned) columns())
+ id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
+ } else
+ id_cols = max_id_len;
+
+ if (!arg_no_legend)
+ printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
+
+ for (u = units; u < units + c; u++) {
+ char *e;
+ const char *on, *off;
+ const char *id;
+
+ if (!output_show_unit_file(u))
+ continue;
+
+ n_shown++;
+
+ if (u->state == UNIT_FILE_MASKED ||
+ u->state == UNIT_FILE_MASKED_RUNTIME ||
+ u->state == UNIT_FILE_DISABLED) {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
+ } else if (u->state == UNIT_FILE_ENABLED) {
+ on = ansi_highlight_green(true);
+ off = ansi_highlight_green(false);
+ } else
+ on = off = "";
+
+ id = file_name_from_path(u->path);
+
+ e = arg_full ? NULL : ellipsize(id, id_cols, 33);
+
+ printf("%-*s %s%-*s%s\n",
+ id_cols, e ? e : id,
+ on, state_cols, unit_file_state_to_string(u->state), off);
+
+ free(e);
+ }
+
+ if (!arg_no_legend)
+ printf("\n%u unit files listed.\n", n_shown);
+}
+
+static int list_unit_files(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ DBusMessageIter iter, sub, sub2;
+ unsigned c = 0, n_units = 0;
+ UnitFileList *units = NULL;
+
+ dbus_error_init(&error);
+
+ pager_open_if_enabled();
+
+ if (avoid_bus()) {
+ Hashmap *h;
+ UnitFileList *u;
+ Iterator i;
+
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ r = unit_file_get_list(arg_scope, arg_root, h);
+ if (r < 0) {
+ unit_file_list_free(h);
+ log_error("Failed to get unit file list: %s", strerror(-r));
+ return r;
+ }
+
+ n_units = hashmap_size(h);
+ units = new(UnitFileList, n_units);
+ if (!units) {
+ unit_file_list_free(h);
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ HASHMAP_FOREACH(u, h, i) {
+ memcpy(units + c++, u, sizeof(UnitFileList));
+ free(u);
+ }
+
+ hashmap_free(h);
+ } else {
+ assert(bus);
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitFiles");
+ if (!m) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ UnitFileList *u;
+ const char *state;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (c >= n_units) {
+ UnitFileList *w;
+
+ n_units = MAX(2*c, 16);
+ w = realloc(units, sizeof(struct UnitFileList) * n_units);
+
+ if (!w) {
+ log_error("Failed to allocate unit array.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ units = w;
+ }
+
+ u = units+c;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ u->state = unit_file_state_from_string(state);
+
+ dbus_message_iter_next(&sub);
+ c++;
+ }
+ }
+
+ if (c > 0) {
+ qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
+ output_unit_file_list(units, c);
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ free(units);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
+ static const char * const colors[] = {
+ "Requires", "[color=\"black\"]",
+ "RequiresOverridable", "[color=\"black\"]",
+ "Requisite", "[color=\"darkblue\"]",
+ "RequisiteOverridable", "[color=\"darkblue\"]",
+ "Wants", "[color=\"darkgrey\"]",
+ "Conflicts", "[color=\"red\"]",
+ "ConflictedBy", "[color=\"red\"]",
+ "After", "[color=\"green\"]"
+ };
+
+ const char *c = NULL;
+ unsigned i;
+
+ assert(name);
+ assert(prop);
+ assert(iter);
+
+ for (i = 0; i < ELEMENTSOF(colors); i += 2)
+ if (streq(colors[i], prop)) {
+ c = colors[i+1];
+ break;
+ }
+
+ if (!c)
+ return 0;
+
+ if (arg_dot != DOT_ALL)
+ if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
+ return 0;
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+
+ case DBUS_TYPE_ARRAY:
+
+ if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *s;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub, &s);
+ printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int dot_one(DBusConnection *bus, const char *name, const char *path) {
+ DBusMessage *m = NULL, *reply = NULL;
+ const char *interface = "org.freedesktop.systemd1.Unit";
+ int r;
+ DBusError error;
+ DBusMessageIter iter, sub, sub2, sub3;
+
+ assert(bus);
+ assert(path);
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *prop;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub2, &sub3);
+
+ if (dot_one_property(name, prop, &sub3)) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int dot(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ DBusMessageIter iter, sub, sub2;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ printf("digraph systemd {\n");
+
+ dbus_message_iter_recurse(&iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if ((r = dot_one(bus, id, unit_path)) < 0)
+ goto finish;
+
+ /* printf("\t\"%s\";\n", id); */
+ dbus_message_iter_next(&sub);
+ }
+
+ printf("}\n");
+
+ log_info(" Color legend: black = Requires\n"
+ " dark blue = Requisite\n"
+ " dark grey = Wants\n"
+ " red = Conflicts\n"
+ " green = After\n");
+
+ if (on_tty())
+ log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
+ "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int list_jobs(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ DBusMessageIter iter, sub, sub2;
+ unsigned k = 0;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+
+ pager_open_if_enabled();
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListJobs"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (on_tty())
+ printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *name, *type, *state, *job_path, *unit_path;
+ uint32_t id;
+ char *e;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ e = arg_full ? NULL : ellipsize(name, 25, 33);
+ printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
+ free(e);
+
+ k++;
+
+ dbus_message_iter_next(&sub);
+ }
+
+ if (on_tty())
+ printf("\n%u jobs listed.\n", k);
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int load_unit(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL;
+ DBusError error;
+ int r;
+ char **name;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+ assert(args);
+
+ STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LoadUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+
+ m = reply = NULL;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int cancel_job(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ char **name;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+ assert(args);
+
+ if (strv_length(args) <= 1)
+ return daemon_reload(bus, args);
+
+ STRV_FOREACH(name, args+1) {
+ unsigned id;
+ const char *path;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetJob"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = safe_atou(*name, &id)) < 0) {
+ log_error("Failed to parse job id: %s", strerror(-r));
+ goto finish;
+ }
+
+ assert_cc(sizeof(uint32_t) == sizeof(id));
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Job",
+ "Cancel"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
+ DBusMessage *m = NULL, *reply = NULL;
+ dbus_bool_t b = FALSE;
+ DBusMessageIter iter, sub;
+ const char
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "NeedDaemonReload",
+ *path;
+
+ /* We ignore all errors here, since this is used to show a warning only */
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit")))
+ goto finish;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_INVALID))
+ goto finish;
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+ goto finish;
+
+ if (!dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto finish;
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get")))
+ goto finish;
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+ goto finish;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ goto finish;
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ goto finish;
+
+ dbus_message_iter_get_basic(&sub, &b);
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return b;
+}
+
+typedef struct WaitData {
+ Set *set;
+ char *result;
+} WaitData;
+
+static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+ DBusError error;
+ WaitData *d = data;
+
+ assert(connection);
+ assert(message);
+ assert(d);
+
+ dbus_error_init(&error);
+
+ 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_error("Warning! D-Bus connection terminated.");
+ dbus_connection_close(connection);
+
+ } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
+ uint32_t id;
+ const char *path, *result;
+ dbus_bool_t success = true;
+
+ if (dbus_message_get_args(message, &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_STRING, &result,
+ DBUS_TYPE_INVALID)) {
+ char *p;
+
+ if ((p = set_remove(d->set, (char*) path)))
+ free(p);
+
+ if (*result)
+ d->result = strdup(result);
+
+ goto finish;
+ }
+#ifndef LEGACY
+ dbus_error_free(&error);
+
+ if (dbus_message_get_args(message, &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_BOOLEAN, &success,
+ DBUS_TYPE_INVALID)) {
+ char *p;
+
+ /* Compatibility with older systemd versions <
+ * 19 during upgrades. This should be dropped
+ * one day */
+
+ if ((p = set_remove(d->set, (char*) path)))
+ free(p);
+
+ if (!success)
+ d->result = strdup("failed");
+
+ goto finish;
+ }
+#endif
+
+ log_error("Failed to parse message: %s", bus_error_message(&error));
+ }
+
+finish:
+ dbus_error_free(&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int enable_wait_for_jobs(DBusConnection *bus) {
+ DBusError error;
+
+ assert(bus);
+
+ if (private_bus)
+ return 0;
+
+ dbus_error_init(&error);
+ dbus_bus_add_match(bus,
+ "type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='JobRemoved',"
+ "path='/org/freedesktop/systemd1'",
+ &error);
+
+ if (dbus_error_is_set(&error)) {
+ log_error("Failed to add match: %s", bus_error_message(&error));
+ dbus_error_free(&error);
+ return -EIO;
+ }
+
+ /* This is slightly dirty, since we don't undo the match registrations. */
+ return 0;
+}
+
+static int wait_for_jobs(DBusConnection *bus, Set *s) {
+ int r;
+ WaitData d;
+
+ assert(bus);
+ assert(s);
+
+ zero(d);
+ d.set = s;
+
+ if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
+ log_error("Failed to add filter.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ while (!set_isempty(s) &&
+ dbus_connection_read_write_dispatch(bus, -1))
+ ;
+
+ if (!arg_quiet && d.result) {
+ if (streq(d.result, "timeout"))
+ log_error("Job timed out.");
+ else if (streq(d.result, "canceled"))
+ log_error("Job canceled.");
+ else if (streq(d.result, "dependency"))
+ log_error("A dependency job failed. See system journal for details.");
+ else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
+ log_error("Job failed. See system journal and 'systemctl status' for details.");
+ }
+
+ if (streq_ptr(d.result, "timeout"))
+ r = -ETIME;
+ else if (streq_ptr(d.result, "canceled"))
+ r = -ECANCELED;
+ else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
+ r = -EIO;
+ else
+ r = 0;
+
+ free(d.result);
+
+finish:
+ /* This is slightly dirty, since we don't undo the filter registration. */
+
+ return r;
+}
+
+static int start_unit_one(
+ DBusConnection *bus,
+ const char *method,
+ const char *name,
+ const char *mode,
+ DBusError *error,
+ Set *s) {
+
+ DBusMessage *m = NULL, *reply = NULL;
+ const char *path;
+ int r;
+
+ assert(bus);
+ assert(method);
+ assert(name);
+ assert(mode);
+ assert(error);
+ assert(arg_no_block || s);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
+
+ if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
+ /* There's always a fallback possible for
+ * legacy actions. */
+ r = -EADDRNOTAVAIL;
+ goto finish;
+ }
+
+ log_error("Failed to issue method call: %s", bus_error_message(error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (need_daemon_reload(bus, name))
+ log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
+ arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
+
+ if (!arg_no_block) {
+ char *p;
+
+ if (!(p = strdup(path))) {
+ log_error("Failed to duplicate path.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((r = set_put(s, p)) < 0) {
+ free(p);
+ log_error("Failed to add path to set.");
+ goto finish;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ return r;
+}
+
+static enum action verb_to_action(const char *verb) {
+ if (streq(verb, "halt"))
+ return ACTION_HALT;
+ else if (streq(verb, "poweroff"))
+ return ACTION_POWEROFF;
+ else if (streq(verb, "reboot"))
+ return ACTION_REBOOT;
+ else if (streq(verb, "kexec"))
+ return ACTION_KEXEC;
+ else if (streq(verb, "rescue"))
+ return ACTION_RESCUE;
+ else if (streq(verb, "emergency"))
+ return ACTION_EMERGENCY;
+ else if (streq(verb, "default"))
+ return ACTION_DEFAULT;
+ else if (streq(verb, "exit"))
+ return ACTION_EXIT;
+ else
+ return ACTION_INVALID;
+}
+
+static int start_unit(DBusConnection *bus, char **args) {
+
+ static const char * const table[_ACTION_MAX] = {
+ [ACTION_HALT] = SPECIAL_HALT_TARGET,
+ [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
+ [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
+ [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
+ [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
+ [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
+ [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
+ [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
+ [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
+ [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
+ [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
+ [ACTION_EXIT] = SPECIAL_EXIT_TARGET
+ };
+
+ int r, ret = 0;
+ const char *method, *mode, *one_name;
+ Set *s = NULL;
+ DBusError error;
+ char **name;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+
+ ask_password_agent_open_if_enabled();
+
+ if (arg_action == ACTION_SYSTEMCTL) {
+ method =
+ streq(args[0], "stop") ||
+ streq(args[0], "condstop") ? "StopUnit" :
+ streq(args[0], "reload") ? "ReloadUnit" :
+ streq(args[0], "restart") ? "RestartUnit" :
+
+ streq(args[0], "try-restart") ||
+ streq(args[0], "condrestart") ? "TryRestartUnit" :
+
+ streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" :
+
+ streq(args[0], "reload-or-try-restart") ||
+ streq(args[0], "condreload") ||
+
+ streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" :
+ "StartUnit";
+
+ mode =
+ (streq(args[0], "isolate") ||
+ streq(args[0], "rescue") ||
+ streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
+
+ one_name = table[verb_to_action(args[0])];
+
+ } else {
+ assert(arg_action < ELEMENTSOF(table));
+ assert(table[arg_action]);
+
+ method = "StartUnit";
+
+ mode = (arg_action == ACTION_EMERGENCY ||
+ arg_action == ACTION_RESCUE ||
+ arg_action == ACTION_RUNLEVEL2 ||
+ arg_action == ACTION_RUNLEVEL3 ||
+ arg_action == ACTION_RUNLEVEL4 ||
+ arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
+
+ one_name = table[arg_action];
+ }
+
+ if (!arg_no_block) {
+ if ((ret = enable_wait_for_jobs(bus)) < 0) {
+ log_error("Could not watch jobs: %s", strerror(-ret));
+ goto finish;
+ }
+
+ if (!(s = set_new(string_hash_func, string_compare_func))) {
+ log_error("Failed to allocate set.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+ }
+
+ if (one_name) {
+ if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
+ goto finish;
+ } else {
+ STRV_FOREACH(name, args+1)
+ if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
+ ret = translate_bus_error_to_exit_status(r, &error);
+ dbus_error_free(&error);
+ }
+ }
+
+ if (!arg_no_block)
+ if ((r = wait_for_jobs(bus, s)) < 0) {
+ ret = r;
+ goto finish;
+ }
+
+finish:
+ if (s)
+ set_free_free(s);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
+static int reboot_with_logind(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ const char *method;
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ dbus_bool_t interactive = true;
+ int r;
+
+ dbus_error_init(&error);
+
+ polkit_agent_open_if_enabled();
+
+ switch (a) {
+
+ case ACTION_REBOOT:
+ method = "Reboot";
+ break;
+
+ case ACTION_POWEROFF:
+ method = "PowerOff";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ method);
+ if (!m) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ if (error_is_no_service(&error)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -ENOENT;
+ goto finish;
+ }
+
+ if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EACCES;
+ goto finish;
+ }
+
+ log_info("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+#else
+ return -ENOSYS;
+#endif
+}
+
+static int start_special(DBusConnection *bus, char **args) {
+ enum action a;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ a = verb_to_action(args[0]);
+
+ if (arg_force >= 2 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT))
+ halt_now(a);
+
+ if (arg_force >= 1 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ||
+ a == ACTION_EXIT))
+ return daemon_reload(bus, args);
+
+ /* first try logind, to allow authentication with polkit */
+ if (geteuid() != 0 &&
+ (a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT)) {
+ r = reboot_with_logind(bus, a);
+ if (r >= 0)
+ return r;
+ }
+
+ r = start_unit(bus, args);
+ if (r >= 0)
+ warn_wall(a);
+
+ return r;
+}
+
+static int check_unit(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ const char
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "ActiveState";
+ int r = 3; /* According to LSB: "program is not running" */
+ DBusError error;
+ char **name;
+
+ assert(bus);
+ assert(args);
+
+ dbus_error_init(&error);
+
+ STRV_FOREACH(name, args+1) {
+ const char *path = NULL;
+ const char *state;
+ DBusMessageIter iter, sub;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+ /* Hmm, cannot figure out anything about this unit... */
+ if (!arg_quiet)
+ puts("unknown");
+
+ dbus_error_free(&error);
+ dbus_message_unref(m);
+ m = NULL;
+ continue;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, &state);
+
+ if (!arg_quiet)
+ puts(state);
+
+ if (streq(state, "active") || streq(state, "reloading"))
+ r = 0;
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int kill_unit(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL;
+ int r = 0;
+ DBusError error;
+ char **name;
+
+ assert(bus);
+ assert(args);
+
+ dbus_error_init(&error);
+
+ if (!arg_kill_who)
+ arg_kill_who = "all";
+
+ if (!arg_kill_mode)
+ arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
+
+ STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "KillUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_STRING, &arg_kill_who,
+ DBUS_TYPE_STRING, &arg_kill_mode,
+ DBUS_TYPE_INT32, &arg_signal,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ dbus_error_free(&error);
+ r = -EIO;
+ }
+
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+typedef struct ExecStatusInfo {
+ char *name;
+
+ char *path;
+ char **argv;
+
+ bool ignore;
+
+ usec_t start_timestamp;
+ usec_t exit_timestamp;
+ pid_t pid;
+ int code;
+ int status;
+
+ LIST_FIELDS(struct ExecStatusInfo, exec);
+} ExecStatusInfo;
+
+static void exec_status_info_free(ExecStatusInfo *i) {
+ assert(i);
+
+ free(i->name);
+ free(i->path);
+ strv_free(i->argv);
+ free(i);
+}
+
+static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
+ uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
+ DBusMessageIter sub2, sub3;
+ const char*path;
+ unsigned n;
+ uint32_t pid;
+ int32_t code, status;
+ dbus_bool_t ignore;
+
+ assert(i);
+ assert(i);
+
+ if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
+ return -EIO;
+
+ dbus_message_iter_recurse(sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
+ return -EIO;
+
+ if (!(i->path = strdup(path)))
+ return -ENOMEM;
+
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
+ return -EIO;
+
+ n = 0;
+ dbus_message_iter_recurse(&sub2, &sub3);
+ while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
+ assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
+ dbus_message_iter_next(&sub3);
+ n++;
+ }
+
+
+ if (!(i->argv = new0(char*, n+1)))
+ return -ENOMEM;
+
+ n = 0;
+ dbus_message_iter_recurse(&sub2, &sub3);
+ while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
+ const char *s;
+
+ assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub3, &s);
+ dbus_message_iter_next(&sub3);
+
+ if (!(i->argv[n++] = strdup(s)))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_next(&sub2) ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp_monotonic, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp_monotonic, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
+ return -EIO;
+
+ i->ignore = ignore;
+ i->start_timestamp = (usec_t) start_timestamp;
+ i->exit_timestamp = (usec_t) exit_timestamp;
+ i->pid = (pid_t) pid;
+ i->code = code;
+ i->status = status;
+
+ return 0;
+}
+
+typedef struct UnitStatusInfo {
+ const char *id;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *unit_file_state;
+
+ const char *description;
+ const char *following;
+
+ const char *path;
+ const char *default_control_group;
+
+ const char *load_error;
+ const char *result;
+
+ usec_t inactive_exit_timestamp;
+ usec_t inactive_exit_timestamp_monotonic;
+ usec_t active_enter_timestamp;
+ usec_t active_exit_timestamp;
+ usec_t inactive_enter_timestamp;
+
+ bool need_daemon_reload;
+
+ /* Service */
+ pid_t main_pid;
+ pid_t control_pid;
+ const char *status_text;
+ bool running:1;
+#ifdef HAVE_SYSV_COMPAT
+ bool is_sysv:1;
+#endif
+
+ usec_t start_timestamp;
+ usec_t exit_timestamp;
+
+ int exit_code, exit_status;
+
+ usec_t condition_timestamp;
+ bool condition_result;
+
+ /* Socket */
+ unsigned n_accepted;
+ unsigned n_connections;
+ bool accept;
+
+ /* Device */
+ const char *sysfs_path;
+
+ /* Mount, Automount */
+ const char *where;
+
+ /* Swap */
+ const char *what;
+
+ LIST_HEAD(ExecStatusInfo, exec);
+} UnitStatusInfo;
+
+static void print_status_info(UnitStatusInfo *i) {
+ ExecStatusInfo *p;
+ const char *on, *off, *ss;
+ usec_t timestamp;
+ char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
+
+ assert(i);
+
+ /* This shows pretty information about a unit. See
+ * print_property() for a low-level property printer */
+
+ printf("%s", strna(i->id));
+
+ if (i->description && !streq_ptr(i->id, i->description))
+ printf(" - %s", i->description);
+
+ printf("\n");
+
+ if (i->following)
+ printf("\t Follow: unit currently follows state of %s\n", i->following);
+
+ if (streq_ptr(i->load_state, "error")) {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
+ } else
+ on = off = "";
+
+ if (i->load_error)
+ printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+ else if (i->path && i->unit_file_state)
+ printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
+ else if (i->path)
+ printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
+ else
+ printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
+
+ ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
+
+ if (streq_ptr(i->active_state, "failed")) {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
+ } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+ on = ansi_highlight_green(true);
+ off = ansi_highlight_green(false);
+ } else
+ on = off = "";
+
+ if (ss)
+ printf("\t Active: %s%s (%s)%s",
+ on,
+ strna(i->active_state),
+ ss,
+ off);
+ else
+ printf("\t Active: %s%s%s",
+ on,
+ strna(i->active_state),
+ off);
+
+ if (!isempty(i->result) && !streq(i->result, "success"))
+ printf(" (Result: %s)", i->result);
+
+ timestamp = (streq_ptr(i->active_state, "active") ||
+ streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
+ (streq_ptr(i->active_state, "inactive") ||
+ streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
+ streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
+
+ s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), timestamp);
+
+ if (s1)
+ printf(" since %s; %s\n", s2, s1);
+ else if (s2)
+ printf(" since %s\n", s2);
+ else
+ printf("\n");
+
+ if (!i->condition_result && i->condition_timestamp > 0) {
+ s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
+
+ if (s1)
+ printf("\t start condition failed at %s; %s\n", s2, s1);
+ else if (s2)
+ printf("\t start condition failed at %s\n", s2);
+ }
+
+ if (i->sysfs_path)
+ printf("\t Device: %s\n", i->sysfs_path);
+ if (i->where)
+ printf("\t Where: %s\n", i->where);
+ if (i->what)
+ printf("\t What: %s\n", i->what);
+
+ if (i->accept)
+ printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
+
+ LIST_FOREACH(exec, p, i->exec) {
+ char *t;
+ bool good;
+
+ /* Only show exited processes here */
+ if (p->code == 0)
+ continue;
+
+ t = strv_join(p->argv, " ");
+ printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
+ free(t);
+
+#ifdef HAVE_SYSV_COMPAT
+ if (i->is_sysv)
+ good = is_clean_exit_lsb(p->code, p->status);
+ else
+#endif
+ good = is_clean_exit(p->code, p->status);
+
+ if (!good) {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
+ } else
+ on = off = "";
+
+ printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
+
+ if (p->code == CLD_EXITED) {
+ const char *c;
+
+ printf("status=%i", p->status);
+
+#ifdef HAVE_SYSV_COMPAT
+ if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+ if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
+#endif
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(p->status));
+
+ printf(")%s\n", off);
+
+ if (i->main_pid == p->pid &&
+ i->start_timestamp == p->start_timestamp &&
+ i->exit_timestamp == p->start_timestamp)
+ /* Let's not show this twice */
+ i->main_pid = 0;
+
+ if (p->pid == i->control_pid)
+ i->control_pid = 0;
+ }
+
+ if (i->main_pid > 0 || i->control_pid > 0) {
+ printf("\t");
+
+ if (i->main_pid > 0) {
+ printf("Main PID: %u", (unsigned) i->main_pid);
+
+ if (i->running) {
+ char *t = NULL;
+ get_process_comm(i->main_pid, &t);
+ if (t) {
+ printf(" (%s)", t);
+ free(t);
+ }
+ } else if (i->exit_code > 0) {
+ printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
+
+ if (i->exit_code == CLD_EXITED) {
+ const char *c;
+
+ printf("status=%i", i->exit_status);
+
+#ifdef HAVE_SYSV_COMPAT
+ if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+ if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
+#endif
+ printf("/%s", c);
+
+ } else
+ printf("signal=%s", signal_to_string(i->exit_status));
+ printf(")");
+ }
+ }
+
+ if (i->main_pid > 0 && i->control_pid > 0)
+ printf(";");
+
+ if (i->control_pid > 0) {
+ char *t = NULL;
+
+ printf(" Control: %u", (unsigned) i->control_pid);
+
+ get_process_comm(i->control_pid, &t);
+ if (t) {
+ printf(" (%s)", t);
+ free(t);
+ }
+ }
+
+ printf("\n");
+ }
+
+ if (i->status_text)
+ printf("\t Status: \"%s\"\n", i->status_text);
+
+ if (i->default_control_group) {
+ unsigned c;
+
+ printf("\t CGroup: %s\n", i->default_control_group);
+
+ if (arg_transport != TRANSPORT_SSH) {
+ if ((c = columns()) > 18)
+ c -= 18;
+ else
+ c = 0;
+
+ show_cgroup_by_path(i->default_control_group, "\t\t ", c, false);
+ }
+ }
+
+ if (i->id && arg_transport != TRANSPORT_SSH) {
+ printf("\n");
+ show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
+ }
+
+ if (i->need_daemon_reload)
+ printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
+ ansi_highlight_red(true),
+ ansi_highlight_red(false),
+ arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
+}
+
+static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
+
+ assert(name);
+ assert(iter);
+ assert(i);
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+
+ case DBUS_TYPE_STRING: {
+ const char *s;
+
+ dbus_message_iter_get_basic(iter, &s);
+
+ if (!isempty(s)) {
+ if (streq(name, "Id"))
+ i->id = s;
+ else if (streq(name, "LoadState"))
+ i->load_state = s;
+ else if (streq(name, "ActiveState"))
+ i->active_state = s;
+ else if (streq(name, "SubState"))
+ i->sub_state = s;
+ else if (streq(name, "Description"))
+ i->description = s;
+ else if (streq(name, "FragmentPath"))
+ i->path = s;
+#ifdef HAVE_SYSV_COMPAT
+ else if (streq(name, "SysVPath")) {
+ i->is_sysv = true;
+ i->path = s;
+ }
+#endif
+ else if (streq(name, "DefaultControlGroup"))
+ i->default_control_group = s;
+ else if (streq(name, "StatusText"))
+ i->status_text = s;
+ else if (streq(name, "SysFSPath"))
+ i->sysfs_path = s;
+ else if (streq(name, "Where"))
+ i->where = s;
+ else if (streq(name, "What"))
+ i->what = s;
+ else if (streq(name, "Following"))
+ i->following = s;
+ else if (streq(name, "UnitFileState"))
+ i->unit_file_state = s;
+ else if (streq(name, "Result"))
+ i->result = s;
+ }
+
+ break;
+ }
+
+ case DBUS_TYPE_BOOLEAN: {
+ dbus_bool_t b;
+
+ dbus_message_iter_get_basic(iter, &b);
+
+ if (streq(name, "Accept"))
+ i->accept = b;
+ else if (streq(name, "NeedDaemonReload"))
+ i->need_daemon_reload = b;
+ else if (streq(name, "ConditionResult"))
+ i->condition_result = b;
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT32: {
+ uint32_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "MainPID")) {
+ if (u > 0) {
+ i->main_pid = (pid_t) u;
+ i->running = true;
+ }
+ } else if (streq(name, "ControlPID"))
+ i->control_pid = (pid_t) u;
+ else if (streq(name, "ExecMainPID")) {
+ if (u > 0)
+ i->main_pid = (pid_t) u;
+ } else if (streq(name, "NAccepted"))
+ i->n_accepted = u;
+ else if (streq(name, "NConnections"))
+ i->n_connections = u;
+
+ break;
+ }
+
+ case DBUS_TYPE_INT32: {
+ int32_t j;
+
+ dbus_message_iter_get_basic(iter, &j);
+
+ if (streq(name, "ExecMainCode"))
+ i->exit_code = (int) j;
+ else if (streq(name, "ExecMainStatus"))
+ i->exit_status = (int) j;
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT64: {
+ uint64_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "ExecMainStartTimestamp"))
+ i->start_timestamp = (usec_t) u;
+ else if (streq(name, "ExecMainExitTimestamp"))
+ i->exit_timestamp = (usec_t) u;
+ else if (streq(name, "ActiveEnterTimestamp"))
+ i->active_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveEnterTimestamp"))
+ i->inactive_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveExitTimestamp"))
+ i->inactive_exit_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveExitTimestampMonotonic"))
+ i->inactive_exit_timestamp_monotonic = (usec_t) u;
+ else if (streq(name, "ActiveExitTimestamp"))
+ i->active_exit_timestamp = (usec_t) u;
+ else if (streq(name, "ConditionTimestamp"))
+ i->condition_timestamp = (usec_t) u;
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+
+ if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
+ startswith(name, "Exec")) {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ ExecStatusInfo *info;
+ int r;
+
+ if (!(info = new0(ExecStatusInfo, 1)))
+ return -ENOMEM;
+
+ if (!(info->name = strdup(name))) {
+ free(info);
+ return -ENOMEM;
+ }
+
+ if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
+ free(info);
+ return r;
+ }
+
+ LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
+
+ dbus_message_iter_next(&sub);
+ }
+ }
+
+ break;
+ }
+
+ case DBUS_TYPE_STRUCT: {
+
+ if (streq(name, "LoadError")) {
+ DBusMessageIter sub;
+ const char *n, *message;
+ int r;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
+ if (r < 0)
+ return r;
+
+ r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
+ if (r < 0)
+ return r;
+
+ if (!isempty(message))
+ i->load_error = message;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int print_property(const char *name, DBusMessageIter *iter) {
+ assert(name);
+ assert(iter);
+
+ /* This is a low-level property printer, see
+ * print_status_info() for the nicer output */
+
+ if (arg_property && !strv_find(arg_property, name))
+ return 0;
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+
+ case DBUS_TYPE_STRUCT: {
+ DBusMessageIter sub;
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
+ uint32_t u;
+
+ dbus_message_iter_get_basic(&sub, &u);
+
+ if (u)
+ printf("%s=%u\n", name, (unsigned) u);
+ else if (arg_all)
+ printf("%s=\n", name);
+
+ return 0;
+ } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
+ const char *s;
+
+ dbus_message_iter_get_basic(&sub, &s);
+
+ if (arg_all || s[0])
+ printf("%s=%s\n", name, s);
+
+ return 0;
+ } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
+ const char *a = NULL, *b = NULL;
+
+ if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
+ bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
+
+ if (arg_all || !isempty(a) || !isempty(b))
+ printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+
+ return 0;
+ }
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY:
+
+ if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *path;
+ dbus_bool_t ignore;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0)
+ printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *type, *path;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
+ printf("%s=%s\n", type, path);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *base;
+ uint64_t value, next_elapse;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
+ char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
+
+ printf("%s={ value=%s ; next_elapse=%s }\n",
+ base,
+ format_timespan(timespan1, sizeof(timespan1), value),
+ format_timespan(timespan2, sizeof(timespan2), next_elapse));
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *controller, *attr, *value;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
+
+ printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
+ controller,
+ attr,
+ value);
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ ExecStatusInfo info;
+
+ zero(info);
+ if (exec_status_info_deserialize(&sub, &info) >= 0) {
+ char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
+ char *t;
+
+ t = strv_join(info.argv, " ");
+
+ printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
+ name,
+ strna(info.path),
+ strna(t),
+ yes_no(info.ignore),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ (unsigned) info. pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+
+ free(t);
+ }
+
+ free(info.path);
+ strv_free(info.argv);
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+ }
+
+ break;
+ }
+
+ if (generic_print_property(name, iter, arg_all) > 0)
+ return 0;
+
+ if (arg_all)
+ printf("%s=[unprintable]\n", name);
+
+ return 0;
+}
+
+static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
+ DBusMessage *m = NULL, *reply = NULL;
+ const char *interface = "";
+ int r;
+ DBusError error;
+ DBusMessageIter iter, sub, sub2, sub3;
+ UnitStatusInfo info;
+ ExecStatusInfo *p;
+
+ assert(bus);
+ assert(path);
+ assert(new_line);
+
+ zero(info);
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (*new_line)
+ printf("\n");
+
+ *new_line = true;
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *name;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub2, &sub3);
+
+ if (show_properties)
+ r = print_property(name, &sub3);
+ else
+ r = status_property(name, &sub3, &info);
+
+ if (r < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ r = 0;
+
+ if (!show_properties)
+ print_status_info(&info);
+
+ if (!streq_ptr(info.active_state, "active") &&
+ !streq_ptr(info.active_state, "reloading") &&
+ streq(verb, "status"))
+ /* According to LSB: "program not running" */
+ r = 3;
+
+ while ((p = info.exec)) {
+ LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
+ exec_status_info_free(p);
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int show(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ int r, ret = 0;
+ DBusError error;
+ bool show_properties, new_line = false;
+ char **name;
+
+ assert(bus);
+ assert(args);
+
+ dbus_error_init(&error);
+
+ show_properties = !streq(args[0], "status");
+
+ if (show_properties)
+ pager_open_if_enabled();
+
+ if (show_properties && strv_length(args) <= 1) {
+ /* If not argument is specified inspect the manager
+ * itself */
+
+ ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
+ goto finish;
+ }
+
+ STRV_FOREACH(name, args+1) {
+ const char *path = NULL;
+ uint32_t id;
+
+ if (safe_atou32(*name, &id) < 0) {
+
+ /* Interpret as unit name */
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "LoadUnit"))) {
+ log_error("Could not allocate message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+ if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ ret = -EIO;
+ goto finish;
+ }
+
+ dbus_error_free(&error);
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit"))) {
+ log_error("Could not allocate message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+
+ if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
+ ret = 4; /* According to LSB: "program or service status is unknown" */
+ else
+ ret = -EIO;
+ goto finish;
+ }
+ }
+
+ } else if (show_properties) {
+
+ /* Interpret as job id */
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetJob"))) {
+ log_error("Could not allocate message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ ret = -EIO;
+ goto finish;
+ }
+ } else {
+
+ /* Interpret as PID */
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitByPID"))) {
+ log_error("Could not allocate message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ ret = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ ret = -EIO;
+ goto finish;
+ }
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ ret = -EIO;
+ goto finish;
+ }
+
+ if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
+ ret = r;
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return ret;
+}
+
+static int dump(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ const char *text;
+
+ dbus_error_init(&error);
+
+ pager_open_if_enabled();
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "Dump"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ fputs(text, stdout);
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int snapshot(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ const char *name = "", *path, *id;
+ dbus_bool_t cleanup = FALSE;
+ DBusMessageIter iter, sub;
+ const char
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "Id";
+
+ dbus_error_init(&error);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "CreateSnapshot"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (strv_length(args) > 1)
+ name = args[1];
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BOOLEAN, &cleanup,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, &id);
+
+ if (!arg_quiet)
+ puts(id);
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int delete_snapshot(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ int r;
+ DBusError error;
+ char **name;
+
+ assert(bus);
+ assert(args);
+
+ dbus_error_init(&error);
+
+ STRV_FOREACH(name, args+1) {
+ const char *path = NULL;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Snapshot",
+ "Remove"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int daemon_reload(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ const char *method;
+
+ dbus_error_init(&error);
+
+ if (arg_action == ACTION_RELOAD)
+ method = "Reload";
+ else if (arg_action == ACTION_REEXEC)
+ method = "Reexecute";
+ else {
+ assert(arg_action == ACTION_SYSTEMCTL);
+
+ method =
+ streq(args[0], "clear-jobs") ||
+ streq(args[0], "cancel") ? "ClearJobs" :
+ streq(args[0], "daemon-reexec") ? "Reexecute" :
+ streq(args[0], "reset-failed") ? "ResetFailed" :
+ streq(args[0], "halt") ? "Halt" :
+ streq(args[0], "poweroff") ? "PowerOff" :
+ streq(args[0], "reboot") ? "Reboot" :
+ streq(args[0], "kexec") ? "KExec" :
+ streq(args[0], "exit") ? "Exit" :
+ /* "daemon-reload" */ "Reload";
+ }
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+ if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
+ /* There's always a fallback possible for
+ * legacy actions. */
+ r = -EADDRNOTAVAIL;
+ goto finish;
+ }
+
+ if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) {
+ /* On reexecution, we expect a disconnect, not
+ * a reply */
+ r = 0;
+ goto finish;
+ }
+
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int reset_failed(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL;
+ int r;
+ DBusError error;
+ char **name;
+
+ assert(bus);
+ dbus_error_init(&error);
+
+ if (strv_length(args) <= 1)
+ return daemon_reload(bus, args);
+
+ STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ResetFailedUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int show_enviroment(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ DBusMessageIter iter, sub, sub2;
+ int r;
+ const char
+ *interface = "org.freedesktop.systemd1.Manager",
+ *property = "Environment";
+
+ dbus_error_init(&error);
+
+ pager_open_if_enabled();
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.DBus.Properties",
+ "Get"))) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
+ const char *text;
+
+ if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub2, &text);
+ printf("%s\n", text);
+
+ dbus_message_iter_next(&sub2);
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int set_environment(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ const char *method;
+ DBusMessageIter iter, sub;
+ char **name;
+
+ dbus_error_init(&error);
+
+ method = streq(args[0], "set-environment")
+ ? "SetEnvironment"
+ : "UnsetEnvironment";
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method))) {
+
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ dbus_message_iter_init_append(m, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ STRV_FOREACH(name, args+1)
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int enable_sysv_units(char **args) {
+ int r = 0;
+
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
+ const char *verb = args[0];
+ unsigned f = 1, t = 1;
+ LookupPaths paths;
+
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ return 0;
+
+ if (!streq(verb, "enable") &&
+ !streq(verb, "disable") &&
+ !streq(verb, "is-enabled"))
+ return 0;
+
+ /* Processes all SysV units, and reshuffles the array so that
+ * afterwards only the native units remain */
+
+ zero(paths);
+ r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+ if (r < 0)
+ return r;
+
+ r = 0;
+
+ for (f = 1; args[f]; f++) {
+ const char *name;
+ char *p;
+ bool found_native = false, found_sysv;
+ unsigned c = 1;
+ const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+ char **k, *l, *q = NULL;
+ int j;
+ pid_t pid;
+ siginfo_t status;
+
+ name = args[f];
+
+ if (!endswith(name, ".service"))
+ continue;
+
+ if (path_is_absolute(name))
+ continue;
+
+ STRV_FOREACH(k, paths.unit_path) {
+ p = NULL;
+
+ if (!isempty(arg_root))
+ asprintf(&p, "%s/%s/%s", arg_root, *k, name);
+ else
+ asprintf(&p, "%s/%s", *k, name);
+
+ if (!p) {
+ log_error("No memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ found_native = access(p, F_OK) >= 0;
+ free(p);
+
+ if (found_native)
+ break;
+ }
+
+ if (found_native)
+ continue;
+
+ p = NULL;
+ if (!isempty(arg_root))
+ asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
+ else
+ asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
+ if (!p) {
+ log_error("No memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ p[strlen(p) - sizeof(".service") + 1] = 0;
+ found_sysv = access(p, F_OK) >= 0;
+
+ if (!found_sysv) {
+ free(p);
+ continue;
+ }
+
+ /* Mark this entry, so that we don't try enabling it as native unit */
+ args[f] = (char*) "";
+
+ log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
+
+ if (!isempty(arg_root))
+ argv[c++] = q = strappend("--root=", arg_root);
+
+ argv[c++] = file_name_from_path(p);
+ argv[c++] =
+ streq(verb, "enable") ? "on" :
+ streq(verb, "disable") ? "off" : "--level=5";
+ argv[c] = NULL;
+
+ l = strv_join((char**)argv, " ");
+ if (!l) {
+ log_error("No memory.");
+ free(q);
+ free(p);
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ log_info("Executing %s", l);
+ free(l);
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ free(p);
+ free(q);
+ r = -errno;
+ goto finish;
+ } else if (pid == 0) {
+ /* Child */
+
+ execv(argv[0], (char**) argv);
+ _exit(EXIT_FAILURE);
+ }
+
+ free(p);
+ free(q);
+
+ j = wait_for_terminate(pid, &status);
+ if (j < 0) {
+ log_error("Failed to wait for child: %s", strerror(-r));
+ r = j;
+ goto finish;
+ }
+
+ if (status.si_code == CLD_EXITED) {
+ if (streq(verb, "is-enabled")) {
+ if (status.si_status == 0) {
+ if (!arg_quiet)
+ puts("enabled");
+ r = 1;
+ } else {
+ if (!arg_quiet)
+ puts("disabled");
+ }
+
+ } else if (status.si_status != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+ } else {
+ r = -EPROTO;
+ goto finish;
+ }
+ }
+
+finish:
+ lookup_paths_free(&paths);
+
+ /* Drop all SysV units */
+ for (f = 1, t = 1; args[f]; f++) {
+
+ if (isempty(args[f]))
+ continue;
+
+ args[t++] = args[f];
+ }
+
+ args[t] = NULL;
+
+#endif
+ return r;
+}
+
+static int enable_unit(DBusConnection *bus, char **args) {
+ const char *verb = args[0];
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0, i;
+ int carries_install_info = -1;
+ DBusMessage *m = NULL, *reply = NULL;
+ int r;
+ DBusError error;
+
+ r = enable_sysv_units(args);
+ if (r < 0)
+ return r;
+
+ if (!args[1])
+ return 0;
+
+ dbus_error_init(&error);
+
+ if (!bus || avoid_bus()) {
+ if (streq(verb, "enable")) {
+ r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "disable"))
+ r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+ else if (streq(verb, "reenable")) {
+ r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "link"))
+ r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ else if (streq(verb, "preset")) {
+ r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "mask"))
+ r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ else if (streq(verb, "unmask"))
+ r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+ else
+ assert_not_reached("Unknown verb");
+
+ if (r < 0) {
+ log_error("Operation failed: %s", strerror(-r));
+ goto finish;
+ }
+
+ if (!arg_quiet) {
+ for (i = 0; i < n_changes; i++) {
+ if (changes[i].type == UNIT_FILE_SYMLINK)
+ log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+ else
+ log_info("rm '%s'", changes[i].path);
+ }
+ }
+
+ } else {
+ const char *method;
+ bool send_force = true, expect_carries_install_info = false;
+ dbus_bool_t a, b;
+ DBusMessageIter iter, sub, sub2;
+
+ if (streq(verb, "enable")) {
+ method = "EnableUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "disable")) {
+ method = "DisableUnitFiles";
+ send_force = false;
+ } else if (streq(verb, "reenable")) {
+ method = "ReenableUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "link"))
+ method = "LinkUnitFiles";
+ else if (streq(verb, "preset")) {
+ method = "PresetUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "mask"))
+ method = "MaskUnitFiles";
+ else if (streq(verb, "unmask")) {
+ method = "UnmaskUnitFiles";
+ send_force = false;
+ } else
+ assert_not_reached("Unknown verb");
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method);
+ if (!m) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_iter_init_append(m, &iter);
+
+ r = bus_append_strv_iter(&iter, args+1);
+ if (r < 0) {
+ log_error("Failed to append unit files.");
+ goto finish;
+ }
+
+ a = arg_runtime;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
+ log_error("Failed to append runtime boolean.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (send_force) {
+ b = arg_force;
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
+ log_error("Failed to append force boolean.");
+ r = -ENOMEM;
+ goto finish;
+ }
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ log_error("Failed to initialize iterator.");
+ goto finish;
+ }
+
+ if (expect_carries_install_info) {
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
+ if (r < 0) {
+ log_error("Failed to parse reply.");
+ goto finish;
+ }
+
+ carries_install_info = b;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *type, *path, *source;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!arg_quiet) {
+ if (streq(type, "symlink"))
+ log_info("ln -s '%s' '%s'", source, path);
+ else
+ log_info("rm '%s'", path);
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ /* Try to reload if enabeld */
+ if (!arg_no_reload)
+ r = daemon_reload(bus, args);
+ }
+
+ if (carries_install_info == 0)
+ log_warning("Warning: unit files do not carry install information. No operation executed.");
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ unit_file_changes_free(changes, n_changes);
+
+ dbus_error_free(&error);
+ return r;
+}
+
+static int unit_is_enabled(DBusConnection *bus, char **args) {
+ DBusError error;
+ int r;
+ DBusMessage *m = NULL, *reply = NULL;
+ bool enabled;
+ char **name;
+
+ dbus_error_init(&error);
+
+ r = enable_sysv_units(args);
+ if (r < 0)
+ return r;
+
+ enabled = r > 0;
+
+ if (!bus || avoid_bus()) {
+
+ STRV_FOREACH(name, args+1) {
+ UnitFileState state;
+
+ state = unit_file_get_state(arg_scope, arg_root, *name);
+ if (state < 0) {
+ r = state;
+ goto finish;
+ }
+
+ if (state == UNIT_FILE_ENABLED ||
+ state == UNIT_FILE_ENABLED_RUNTIME ||
+ state == UNIT_FILE_STATIC)
+ enabled = true;
+
+ if (!arg_quiet)
+ puts(unit_file_state_to_string(state));
+ }
+
+ } else {
+ STRV_FOREACH(name, args+1) {
+ const char *s;
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitFileState");
+ if (!m) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+
+ if (streq(s, "enabled") ||
+ streq(s, "enabled-runtime") ||
+ streq(s, "static"))
+ enabled = true;
+
+ if (!arg_quiet)
+ puts(s);
+ }
+ }
+
+ r = enabled ? 0 : 1;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+ return r;
+}
+
+static int systemctl_help(void) {
+
+ pager_open_if_enabled();
+
+ printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+ "Query or send control commands to the systemd manager.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -t --type=TYPE List only units of a particular type\n"
+ " -p --property=NAME Show only properties by this name\n"
+ " -a --all Show all units/properties, including dead/empty ones\n"
+ " --failed Show only failed units\n"
+ " --full Don't ellipsize unit names on output\n"
+ " --fail When queueing a new job, fail if conflicting jobs are\n"
+ " pending\n"
+ " --ignore-dependencies\n"
+ " When queueing a new job, ignore all its dependencies\n"
+ " --kill-who=WHO Who to send signal to\n"
+ " -s --signal=SIGNAL Which signal to send\n"
+ " -H --host=[USER@]HOST\n"
+ " Show information for remote host\n"
+ " -P --privileged Acquire privileges before execution\n"
+ " -q --quiet Suppress output\n"
+ " --no-block Do not wait until operation finished\n"
+ " --no-wall Don't send wall message before halt/power-off/reboot\n"
+ " --no-reload When enabling/disabling unit files, don't reload daemon\n"
+ " configuration\n"
+ " --no-legend Do not print a legend (column headers and hints)\n"
+ " --no-pager Do not pipe output into a pager\n"
+ " --no-ask-password\n"
+ " Do not ask for system passwords\n"
+ " --order When generating graph for dot, show only order\n"
+ " --require When generating graph for dot, show only requirement\n"
+ " --system Connect to system manager\n"
+ " --user Connect to user service manager\n"
+ " --global Enable/disable unit files globally\n"
+ " -f --force When enabling unit files, override existing symlinks\n"
+ " When shutting down, execute action immediately\n"
+ " --root=PATH Enable unit files in the specified root directory\n"
+ " --runtime Enable unit files only temporarily until next reboot\n"
+ " -n --lines=INTEGER Journal entries to show\n"
+ " --follow Follow journal\n"
+ " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
+ " verbose, export, json, cat)\n\n"
+ "Unit Commands:\n"
+ " list-units List loaded units\n"
+ " start [NAME...] Start (activate) one or more units\n"
+ " stop [NAME...] Stop (deactivate) one or more units\n"
+ " reload [NAME...] Reload one or more units\n"
+ " restart [NAME...] Start or restart one or more units\n"
+ " try-restart [NAME...] Restart one or more units if active\n"
+ " reload-or-restart [NAME...] Reload one or more units is possible,\n"
+ " otherwise start or restart\n"
+ " reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
+ " otherwise restart if active\n"
+ " isolate [NAME] Start one unit and stop all others\n"
+ " kill [NAME...] Send signal to processes of a unit\n"
+ " is-active [NAME...] Check whether units are active\n"
+ " status [NAME...|PID...] Show runtime status of one or more units\n"
+ " show [NAME...|JOB...] Show properties of one or more\n"
+ " units/jobs or the manager\n"
+ " reset-failed [NAME...] Reset failed state for all, one, or more\n"
+ " units\n"
+ " load [NAME...] Load one or more units\n\n"
+ "Unit File Commands:\n"
+ " list-unit-files List installed unit files\n"
+ " enable [NAME...] Enable one or more unit files\n"
+ " disable [NAME...] Disable one or more unit files\n"
+ " reenable [NAME...] Reenable one or more unit files\n"
+ " preset [NAME...] Enable/disable one or more unit files\n"
+ " based on preset configuration\n"
+ " mask [NAME...] Mask one or more units\n"
+ " unmask [NAME...] Unmask one or more units\n"
+ " link [PATH...] Link one or more units files into\n"
+ " the search path\n"
+ " is-enabled [NAME...] Check whether unit files are enabled\n\n"
+ "Job Commands:\n"
+ " list-jobs List jobs\n"
+ " cancel [JOB...] Cancel all, one, or more jobs\n\n"
+ "Status Commands:\n"
+ " dump Dump server status\n"
+ " dot Dump dependency graph for dot(1)\n\n"
+ "Snapshot Commands:\n"
+ " snapshot [NAME] Create a snapshot\n"
+ " delete [NAME...] Remove one or more snapshots\n\n"
+ "Environment Commands:\n"
+ " show-environment Dump environment\n"
+ " set-environment [NAME=VALUE...] Set one or more environment variables\n"
+ " unset-environment [NAME...] Unset one or more environment variables\n\n"
+ "Manager Lifecycle Commands:\n"
+ " daemon-reload Reload systemd manager configuration\n"
+ " daemon-reexec Reexecute systemd manager\n\n"
+ "System Commands:\n"
+ " default Enter system default mode\n"
+ " rescue Enter system rescue mode\n"
+ " emergency Enter system emergency mode\n"
+ " halt Shut down and halt the system\n"
+ " poweroff Shut down and power-off the system\n"
+ " reboot Shut down and reboot the system\n"
+ " kexec Shut down and reboot the system with kexec\n"
+ " exit Ask for user instance termination\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int halt_help(void) {
+
+ printf("%s [OPTIONS...]\n\n"
+ "%s the system.\n\n"
+ " --help Show this help\n"
+ " --halt Halt the machine\n"
+ " -p --poweroff Switch off the machine\n"
+ " --reboot Reboot the machine\n"
+ " -f --force Force immediate halt/power-off/reboot\n"
+ " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
+ " -d --no-wtmp Don't write wtmp record\n"
+ " -n --no-sync Don't sync before halt/power-off/reboot\n"
+ " --no-wall Don't send wall message before halt/power-off/reboot\n",
+ program_invocation_short_name,
+ arg_action == ACTION_REBOOT ? "Reboot" :
+ arg_action == ACTION_POWEROFF ? "Power off" :
+ "Halt");
+
+ return 0;
+}
+
+static int shutdown_help(void) {
+
+ printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
+ "Shut down the system.\n\n"
+ " --help Show this help\n"
+ " -H --halt Halt the machine\n"
+ " -P --poweroff Power-off the machine\n"
+ " -r --reboot Reboot the machine\n"
+ " -h Equivalent to --poweroff, overriden by --halt\n"
+ " -k Don't halt/power-off/reboot, just send warnings\n"
+ " --no-wall Don't send wall message before halt/power-off/reboot\n"
+ " -c Cancel a pending shutdown\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int telinit_help(void) {
+
+ printf("%s [OPTIONS...] {COMMAND}\n\n"
+ "Send control commands to the init daemon.\n\n"
+ " --help Show this help\n"
+ " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
+ "Commands:\n"
+ " 0 Power-off the machine\n"
+ " 6 Reboot the machine\n"
+ " 2, 3, 4, 5 Start runlevelX.target unit\n"
+ " 1, s, S Enter rescue mode\n"
+ " q, Q Reload init daemon configuration\n"
+ " u, U Reexecute init daemon\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int runlevel_help(void) {
+
+ printf("%s [OPTIONS...]\n\n"
+ "Prints the previous and current runlevel of the init system.\n\n"
+ " --help Show this help\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int systemctl_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_FAIL = 0x100,
+ ARG_IGNORE_DEPENDENCIES,
+ ARG_VERSION,
+ ARG_USER,
+ ARG_SYSTEM,
+ ARG_GLOBAL,
+ ARG_NO_BLOCK,
+ ARG_NO_LEGEND,
+ ARG_NO_PAGER,
+ ARG_NO_WALL,
+ ARG_ORDER,
+ ARG_REQUIRE,
+ ARG_ROOT,
+ ARG_FULL,
+ ARG_NO_RELOAD,
+ ARG_KILL_MODE,
+ ARG_KILL_WHO,
+ ARG_NO_ASK_PASSWORD,
+ ARG_FAILED,
+ ARG_RUNTIME,
+ ARG_FOLLOW,
+ ARG_FORCE
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "type", required_argument, NULL, 't' },
+ { "property", required_argument, NULL, 'p' },
+ { "all", no_argument, NULL, 'a' },
+ { "failed", no_argument, NULL, ARG_FAILED },
+ { "full", no_argument, NULL, ARG_FULL },
+ { "fail", no_argument, NULL, ARG_FAIL },
+ { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
+ { "user", no_argument, NULL, ARG_USER },
+ { "system", no_argument, NULL, ARG_SYSTEM },
+ { "global", no_argument, NULL, ARG_GLOBAL },
+ { "no-block", no_argument, NULL, ARG_NO_BLOCK },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { "quiet", no_argument, NULL, 'q' },
+ { "order", no_argument, NULL, ARG_ORDER },
+ { "require", no_argument, NULL, ARG_REQUIRE },
+ { "root", required_argument, NULL, ARG_ROOT },
+ { "force", no_argument, NULL, ARG_FORCE },
+ { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
+ { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
+ { "kill-who", required_argument, NULL, ARG_KILL_WHO },
+ { "signal", required_argument, NULL, 's' },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "host", required_argument, NULL, 'H' },
+ { "privileged",no_argument, NULL, 'P' },
+ { "runtime", no_argument, NULL, ARG_RUNTIME },
+ { "lines", required_argument, NULL, 'n' },
+ { "follow", no_argument, NULL, ARG_FOLLOW },
+ { "output", required_argument, NULL, 'o' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ systemctl_help();
+ return 0;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(DISTRIBUTION);
+ puts(SYSTEMD_FEATURES);
+ return 0;
+
+ case 't':
+ arg_type = optarg;
+ break;
+
+ case 'p': {
+ char **l;
+
+ if (!(l = strv_append(arg_property, optarg)))
+ return -ENOMEM;
+
+ strv_free(arg_property);
+ arg_property = l;
+
+ /* If the user asked for a particular
+ * property, show it to him, even if it is
+ * empty. */
+ arg_all = true;
+ break;
+ }
+
+ case 'a':
+ arg_all = true;
+ break;
+
+ case ARG_FAIL:
+ arg_job_mode = "fail";
+ break;
+
+ case ARG_IGNORE_DEPENDENCIES:
+ arg_job_mode = "ignore-dependencies";
+ break;
+
+ case ARG_USER:
+ arg_scope = UNIT_FILE_USER;
+ break;
+
+ case ARG_SYSTEM:
+ arg_scope = UNIT_FILE_SYSTEM;
+ break;
+
+ case ARG_GLOBAL:
+ arg_scope = UNIT_FILE_GLOBAL;
+ break;
+
+ case ARG_NO_BLOCK:
+ arg_no_block = true;
+ break;
+
+ case ARG_NO_LEGEND:
+ arg_no_legend = true;
+ break;
+
+ case ARG_NO_PAGER:
+ arg_no_pager = true;
+ break;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case ARG_ORDER:
+ arg_dot = DOT_ORDER;
+ break;
+
+ case ARG_REQUIRE:
+ arg_dot = DOT_REQUIRE;
+ break;
+
+ case ARG_ROOT:
+ arg_root = optarg;
+ break;
+
+ case ARG_FULL:
+ arg_full = true;
+ break;
+
+ case ARG_FAILED:
+ arg_failed = true;
+ break;
+
+ case 'q':
+ arg_quiet = true;
+ break;
+
+ case ARG_FORCE:
+ arg_force ++;
+ break;
+
+ case ARG_FOLLOW:
+ arg_follow = true;
+ break;
+
+ case 'f':
+ /* -f is short for both --follow and --force! */
+ arg_force ++;
+ arg_follow = true;
+ break;
+
+ case ARG_NO_RELOAD:
+ arg_no_reload = true;
+ break;
+
+ case ARG_KILL_WHO:
+ arg_kill_who = optarg;
+ break;
+
+ case ARG_KILL_MODE:
+ arg_kill_mode = optarg;
+ break;
+
+ case 's':
+ if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
+ log_error("Failed to parse signal string %s.", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case 'P':
+ arg_transport = TRANSPORT_POLKIT;
+ break;
+
+ case 'H':
+ arg_transport = TRANSPORT_SSH;
+ arg_host = optarg;
+ break;
+
+ case ARG_RUNTIME:
+ arg_runtime = true;
+ break;
+
+ case 'n':
+ if (safe_atou(optarg, &arg_lines) < 0) {
+ log_error("Failed to parse lines '%s'", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case 'o':
+ arg_output = output_mode_from_string(optarg);
+ if (arg_output < 0) {
+ log_error("Unknown output '%s'.", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
+ log_error("Cannot access user instance remotely.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int halt_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ ARG_HALT,
+ ARG_REBOOT,
+ ARG_NO_WALL
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { "halt", no_argument, NULL, ARG_HALT },
+ { "poweroff", no_argument, NULL, 'p' },
+ { "reboot", no_argument, NULL, ARG_REBOOT },
+ { "force", no_argument, NULL, 'f' },
+ { "wtmp-only", no_argument, NULL, 'w' },
+ { "no-wtmp", no_argument, NULL, 'd' },
+ { "no-sync", no_argument, NULL, 'n' },
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c, runlevel;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ if (utmp_get_runlevel(&runlevel, NULL) >= 0)
+ if (runlevel == '0' || runlevel == '6')
+ arg_immediate = true;
+
+ while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ halt_help();
+ return 0;
+
+ case ARG_HALT:
+ arg_action = ACTION_HALT;
+ break;
+
+ case 'p':
+ if (arg_action != ACTION_REBOOT)
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case ARG_REBOOT:
+ arg_action = ACTION_REBOOT;
+ break;
+
+ case 'f':
+ arg_immediate = true;
+ break;
+
+ case 'w':
+ arg_dry = true;
+ break;
+
+ case 'd':
+ arg_no_wtmp = true;
+ break;
+
+ case 'n':
+ arg_no_sync = true;
+ break;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case 'i':
+ case 'h':
+ /* Compatibility nops */
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int parse_time_spec(const char *t, usec_t *_u) {
+ assert(t);
+ assert(_u);
+
+ if (streq(t, "now"))
+ *_u = 0;
+ else if (!strchr(t, ':')) {
+ uint64_t u;
+
+ if (safe_atou64(t, &u) < 0)
+ return -EINVAL;
+
+ *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
+ } else {
+ char *e = NULL;
+ long hour, minute;
+ struct tm tm;
+ time_t s;
+ usec_t n;
+
+ errno = 0;
+ hour = strtol(t, &e, 10);
+ if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
+ return -EINVAL;
+
+ minute = strtol(e+1, &e, 10);
+ if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
+ return -EINVAL;
+
+ n = now(CLOCK_REALTIME);
+ s = (time_t) (n / USEC_PER_SEC);
+
+ zero(tm);
+ assert_se(localtime_r(&s, &tm));
+
+ tm.tm_hour = (int) hour;
+ tm.tm_min = (int) minute;
+ tm.tm_sec = 0;
+
+ assert_se(s = mktime(&tm));
+
+ *_u = (usec_t) s * USEC_PER_SEC;
+
+ while (*_u <= n)
+ *_u += USEC_PER_DAY;
+ }
+
+ return 0;
+}
+
+static int shutdown_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ ARG_NO_WALL
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { "halt", no_argument, NULL, 'H' },
+ { "poweroff", no_argument, NULL, 'P' },
+ { "reboot", no_argument, NULL, 'r' },
+ { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ shutdown_help();
+ return 0;
+
+ case 'H':
+ arg_action = ACTION_HALT;
+ break;
+
+ case 'P':
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case 'r':
+ if (kexec_loaded())
+ arg_action = ACTION_KEXEC;
+ else
+ arg_action = ACTION_REBOOT;
+ break;
+
+ case 'K':
+ arg_action = ACTION_KEXEC;
+ break;
+
+ case 'h':
+ if (arg_action != ACTION_HALT)
+ arg_action = ACTION_POWEROFF;
+ break;
+
+ case 'k':
+ arg_dry = true;
+ break;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case 't':
+ case 'a':
+ /* Compatibility nops */
+ break;
+
+ case 'c':
+ arg_action = ACTION_CANCEL_SHUTDOWN;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (argc > optind) {
+ r = parse_time_spec(argv[optind], &arg_when);
+ if (r < 0) {
+ log_error("Failed to parse time specification: %s", argv[optind]);
+ return r;
+ }
+ } else
+ arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
+
+ /* We skip the time argument */
+ if (argc > optind + 1)
+ arg_wall = argv + optind + 1;
+
+ optind = argc;
+
+ return 1;
+}
+
+static int telinit_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ ARG_NO_WALL
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { "no-wall", no_argument, NULL, ARG_NO_WALL },
+ { NULL, 0, NULL, 0 }
+ };
+
+ static const struct {
+ char from;
+ enum action to;
+ } table[] = {
+ { '0', ACTION_POWEROFF },
+ { '6', ACTION_REBOOT },
+ { '1', ACTION_RESCUE },
+ { '2', ACTION_RUNLEVEL2 },
+ { '3', ACTION_RUNLEVEL3 },
+ { '4', ACTION_RUNLEVEL4 },
+ { '5', ACTION_RUNLEVEL5 },
+ { 's', ACTION_RESCUE },
+ { 'S', ACTION_RESCUE },
+ { 'q', ACTION_RELOAD },
+ { 'Q', ACTION_RELOAD },
+ { 'u', ACTION_REEXEC },
+ { 'U', ACTION_REEXEC }
+ };
+
+ unsigned i;
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ telinit_help();
+ return 0;
+
+ case ARG_NO_WALL:
+ arg_no_wall = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind >= argc) {
+ telinit_help();
+ return -EINVAL;
+ }
+
+ if (optind + 1 < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ if (strlen(argv[optind]) != 1) {
+ log_error("Expected single character argument.");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (table[i].from == argv[optind][0])
+ break;
+
+ if (i >= ELEMENTSOF(table)) {
+ log_error("Unknown command %s.", argv[optind]);
+ return -EINVAL;
+ }
+
+ arg_action = table[i].to;
+
+ optind ++;
+
+ return 1;
+}
+
+static int runlevel_parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_HELP = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, ARG_HELP },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+ switch (c) {
+
+ case ARG_HELP:
+ runlevel_help();
+ return 0;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind < argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ assert(argc >= 0);
+ assert(argv);
+
+ if (program_invocation_short_name) {
+
+ if (strstr(program_invocation_short_name, "halt")) {
+ arg_action = ACTION_HALT;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "poweroff")) {
+ arg_action = ACTION_POWEROFF;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "reboot")) {
+ if (kexec_loaded())
+ arg_action = ACTION_KEXEC;
+ else
+ arg_action = ACTION_REBOOT;
+ return halt_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "shutdown")) {
+ arg_action = ACTION_POWEROFF;
+ return shutdown_parse_argv(argc, argv);
+ } else if (strstr(program_invocation_short_name, "init")) {
+
+ if (sd_booted() > 0) {
+ arg_action = ACTION_INVALID;
+ return telinit_parse_argv(argc, argv);
+ } else {
+ /* Hmm, so some other init system is
+ * running, we need to forward this
+ * request to it. For now we simply
+ * guess that it is Upstart. */
+
+ execv("/lib/upstart/telinit", argv);
+
+ log_error("Couldn't find an alternative telinit implementation to spawn.");
+ return -EIO;
+ }
+
+ } else if (strstr(program_invocation_short_name, "runlevel")) {
+ arg_action = ACTION_RUNLEVEL;
+ return runlevel_parse_argv(argc, argv);
+ }
+ }
+
+ arg_action = ACTION_SYSTEMCTL;
+ return systemctl_parse_argv(argc, argv);
+}
+
+static int action_to_runlevel(void) {
+
+ static const char table[_ACTION_MAX] = {
+ [ACTION_HALT] = '0',
+ [ACTION_POWEROFF] = '0',
+ [ACTION_REBOOT] = '6',
+ [ACTION_RUNLEVEL2] = '2',
+ [ACTION_RUNLEVEL3] = '3',
+ [ACTION_RUNLEVEL4] = '4',
+ [ACTION_RUNLEVEL5] = '5',
+ [ACTION_RESCUE] = '1'
+ };
+
+ assert(arg_action < _ACTION_MAX);
+
+ return table[arg_action];
+}
+
+static int talk_upstart(void) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int previous, rl, r;
+ char
+ env1_buf[] = "RUNLEVEL=X",
+ env2_buf[] = "PREVLEVEL=X";
+ char *env1 = env1_buf, *env2 = env2_buf;
+ const char *emit = "runlevel";
+ dbus_bool_t b_false = FALSE;
+ DBusMessageIter iter, sub;
+ DBusConnection *bus;
+
+ dbus_error_init(&error);
+
+ if (!(rl = action_to_runlevel()))
+ return 0;
+
+ if (utmp_get_runlevel(&previous, NULL) < 0)
+ previous = 'N';
+
+ if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
+ if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
+ r = 0;
+ goto finish;
+ }
+
+ log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if ((r = bus_check_peercred(bus)) < 0) {
+ log_error("Failed to verify owner of bus.");
+ goto finish;
+ }
+
+ if (!(m = dbus_message_new_method_call(
+ "com.ubuntu.Upstart",
+ "/com/ubuntu/Upstart",
+ "com.ubuntu.Upstart0_6",
+ "EmitEvent"))) {
+
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_iter_init_append(m, &iter);
+
+ env1_buf[sizeof(env1_buf)-2] = rl;
+ env2_buf[sizeof(env2_buf)-2] = previous;
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
+ !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
+ !dbus_message_iter_close_container(&iter, &sub) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+ if (error_is_no_service(&error)) {
+ r = -EADDRNOTAVAIL;
+ goto finish;
+ }
+
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 1;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (bus) {
+ dbus_connection_flush(bus);
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int talk_initctl(void) {
+ struct init_request request;
+ int r, fd;
+ char rl;
+
+ if (!(rl = action_to_runlevel()))
+ return 0;
+
+ zero(request);
+ request.magic = INIT_MAGIC;
+ request.sleeptime = 0;
+ request.cmd = INIT_CMD_RUNLVL;
+ request.runlevel = rl;
+
+ if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
+
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open "INIT_FIFO": %m");
+ return -errno;
+ }
+
+ errno = 0;
+ r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
+ close_nointr_nofail(fd);
+
+ if (r < 0) {
+ log_error("Failed to write to "INIT_FIFO": %m");
+ return errno ? -errno : -EIO;
+ }
+
+ return 1;
+}
+
+static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
+
+ static const struct {
+ const char* verb;
+ const enum {
+ MORE,
+ LESS,
+ EQUAL
+ } argc_cmp;
+ const int argc;
+ int (* const dispatch)(DBusConnection *bus, char **args);
+ } verbs[] = {
+ { "list-units", LESS, 1, list_units },
+ { "list-unit-files", EQUAL, 1, list_unit_files },
+ { "list-jobs", EQUAL, 1, list_jobs },
+ { "clear-jobs", EQUAL, 1, daemon_reload },
+ { "load", MORE, 2, load_unit },
+ { "cancel", MORE, 2, cancel_job },
+ { "start", MORE, 2, start_unit },
+ { "stop", MORE, 2, start_unit },
+ { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
+ { "reload", MORE, 2, start_unit },
+ { "restart", MORE, 2, start_unit },
+ { "try-restart", MORE, 2, start_unit },
+ { "reload-or-restart", MORE, 2, start_unit },
+ { "reload-or-try-restart", MORE, 2, start_unit },
+ { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
+ { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
+ { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
+ { "isolate", EQUAL, 2, start_unit },
+ { "kill", MORE, 2, kill_unit },
+ { "is-active", MORE, 2, check_unit },
+ { "check", MORE, 2, check_unit },
+ { "show", MORE, 1, show },
+ { "status", MORE, 2, show },
+ { "dump", EQUAL, 1, dump },
+ { "dot", EQUAL, 1, dot },
+ { "snapshot", LESS, 2, snapshot },
+ { "delete", MORE, 2, delete_snapshot },
+ { "daemon-reload", EQUAL, 1, daemon_reload },
+ { "daemon-reexec", EQUAL, 1, daemon_reload },
+ { "show-environment", EQUAL, 1, show_enviroment },
+ { "set-environment", MORE, 2, set_environment },
+ { "unset-environment", MORE, 2, set_environment },
+ { "halt", EQUAL, 1, start_special },
+ { "poweroff", EQUAL, 1, start_special },
+ { "reboot", EQUAL, 1, start_special },
+ { "kexec", EQUAL, 1, start_special },
+ { "default", EQUAL, 1, start_special },
+ { "rescue", EQUAL, 1, start_special },
+ { "emergency", EQUAL, 1, start_special },
+ { "exit", EQUAL, 1, start_special },
+ { "reset-failed", MORE, 1, reset_failed },
+ { "enable", MORE, 2, enable_unit },
+ { "disable", MORE, 2, enable_unit },
+ { "is-enabled", MORE, 2, unit_is_enabled },
+ { "reenable", MORE, 2, enable_unit },
+ { "preset", MORE, 2, enable_unit },
+ { "mask", MORE, 2, enable_unit },
+ { "unmask", MORE, 2, enable_unit },
+ { "link", MORE, 2, enable_unit }
+ };
+
+ int left;
+ unsigned i;
+
+ assert(argc >= 0);
+ assert(argv);
+ assert(error);
+
+ left = argc - optind;
+
+ if (left <= 0)
+ /* Special rule: no arguments means "list-units" */
+ i = 0;
+ else {
+ if (streq(argv[optind], "help")) {
+ systemctl_help();
+ return 0;
+ }
+
+ for (i = 0; i < ELEMENTSOF(verbs); i++)
+ if (streq(argv[optind], verbs[i].verb))
+ break;
+
+ if (i >= ELEMENTSOF(verbs)) {
+ log_error("Unknown operation %s", argv[optind]);
+ return -EINVAL;
+ }
+ }
+
+ switch (verbs[i].argc_cmp) {
+
+ case EQUAL:
+ if (left != verbs[i].argc) {
+ log_error("Invalid number of arguments.");
+ return -EINVAL;
+ }
+
+ break;
+
+ case MORE:
+ if (left < verbs[i].argc) {
+ log_error("Too few arguments.");
+ return -EINVAL;
+ }
+
+ break;
+
+ case LESS:
+ if (left > verbs[i].argc) {
+ log_error("Too many arguments.");
+ return -EINVAL;
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Unknown comparison operator.");
+ }
+
+ /* Require a bus connection for all operations but
+ * enable/disable */
+ if (!streq(verbs[i].verb, "enable") &&
+ !streq(verbs[i].verb, "disable") &&
+ !streq(verbs[i].verb, "is-enabled") &&
+ !streq(verbs[i].verb, "list-unit-files") &&
+ !streq(verbs[i].verb, "reenable") &&
+ !streq(verbs[i].verb, "preset") &&
+ !streq(verbs[i].verb, "mask") &&
+ !streq(verbs[i].verb, "unmask") &&
+ !streq(verbs[i].verb, "link")) {
+
+ if (running_in_chroot() > 0) {
+ log_info("Running in chroot, ignoring request.");
+ return 0;
+ }
+
+ if (!bus) {
+ log_error("Failed to get D-Bus connection: %s",
+ dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+ return -EIO;
+ }
+
+ } else {
+
+ if (!bus && !avoid_bus()) {
+ log_error("Failed to get D-Bus connection: %s",
+ dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+ return -EIO;
+ }
+ }
+
+ return verbs[i].dispatch(bus, argv + optind);
+}
+
+static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
+ int fd;
+ struct msghdr msghdr;
+ struct iovec iovec[2];
+ union sockaddr_union sockaddr;
+ struct sd_shutdown_command c;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ zero(c);
+ c.usec = t;
+ c.mode = mode;
+ c.dry_run = dry_run;
+ c.warn_wall = warn;
+
+ zero(sockaddr);
+ sockaddr.sa.sa_family = AF_UNIX;
+ strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
+
+ zero(msghdr);
+ msghdr.msg_name = &sockaddr;
+ msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
+
+ zero(iovec);
+ iovec[0].iov_base = (char*) &c;
+ iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
+
+ if (isempty(message))
+ msghdr.msg_iovlen = 1;
+ else {
+ iovec[1].iov_base = (char*) message;
+ iovec[1].iov_len = strlen(message);
+ msghdr.msg_iovlen = 2;
+ }
+ msghdr.msg_iov = iovec;
+
+ if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ close_nointr_nofail(fd);
+ return 0;
+}
+
+static int reload_with_fallback(DBusConnection *bus) {
+
+ if (bus) {
+ /* First, try systemd via D-Bus. */
+ if (daemon_reload(bus, NULL) >= 0)
+ return 0;
+ }
+
+ /* Nothing else worked, so let's try signals */
+ assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
+
+ if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
+ log_error("kill() failed: %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int start_with_fallback(DBusConnection *bus) {
+
+ if (bus) {
+ /* First, try systemd via D-Bus. */
+ if (start_unit(bus, NULL) >= 0)
+ goto done;
+ }
+
+ /* Hmm, talking to systemd via D-Bus didn't work. Then
+ * let's try to talk to Upstart via D-Bus. */
+ if (talk_upstart() > 0)
+ goto done;
+
+ /* Nothing else worked, so let's try
+ * /dev/initctl */
+ if (talk_initctl() > 0)
+ goto done;
+
+ log_error("Failed to talk to init daemon.");
+ return -EIO;
+
+done:
+ warn_wall(arg_action);
+ return 0;
+}
+
+static void halt_now(enum action a) {
+
+ /* Make sure C-A-D is handled by the kernel from this
+ * point on... */
+ reboot(RB_ENABLE_CAD);
+
+ switch (a) {
+
+ case ACTION_HALT:
+ log_info("Halting.");
+ reboot(RB_HALT_SYSTEM);
+ break;
+
+ case ACTION_POWEROFF:
+ log_info("Powering off.");
+ reboot(RB_POWER_OFF);
+ break;
+
+ case ACTION_REBOOT:
+ log_info("Rebooting.");
+ reboot(RB_AUTOBOOT);
+ break;
+
+ default:
+ assert_not_reached("Unknown halt action.");
+ }
+
+ assert_not_reached("Uh? This shouldn't happen.");
+}
+
+static int halt_main(DBusConnection *bus) {
+ int r;
+
+ if (geteuid() != 0) {
+ /* Try logind if we are a normal user and no special
+ * mode applies. Maybe PolicyKit allows us to shutdown
+ * the machine. */
+
+ if (arg_when <= 0 &&
+ !arg_dry &&
+ !arg_immediate &&
+ (arg_action == ACTION_POWEROFF ||
+ arg_action == ACTION_REBOOT)) {
+ r = reboot_with_logind(bus, arg_action);
+ if (r >= 0)
+ return r;
+ }
+
+ log_error("Must be root.");
+ return -EPERM;
+ }
+
+ if (arg_when > 0) {
+ char *m;
+
+ m = strv_join(arg_wall, " ");
+ r = send_shutdownd(arg_when,
+ arg_action == ACTION_HALT ? 'H' :
+ arg_action == ACTION_POWEROFF ? 'P' :
+ arg_action == ACTION_KEXEC ? 'K' :
+ 'r',
+ arg_dry,
+ !arg_no_wall,
+ m);
+ free(m);
+
+ if (r < 0)
+ log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
+ else {
+ char date[FORMAT_TIMESTAMP_MAX];
+
+ log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
+ format_timestamp(date, sizeof(date), arg_when));
+ return 0;
+ }
+ }
+
+ if (!arg_dry && !arg_immediate)
+ return start_with_fallback(bus);
+
+ if (!arg_no_wtmp) {
+ if (sd_booted() > 0)
+ log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
+ else {
+ r = utmp_put_shutdown();
+ if (r < 0)
+ log_warning("Failed to write utmp record: %s", strerror(-r));
+ }
+ }
+
+ if (!arg_no_sync)
+ sync();
+
+ if (arg_dry)
+ return 0;
+
+ halt_now(arg_action);
+ /* We should never reach this. */
+ return -ENOSYS;
+}
+
+static int runlevel_main(void) {
+ int r, runlevel, previous;
+
+ r = utmp_get_runlevel(&runlevel, &previous);
+ if (r < 0) {
+ puts("unknown");
+ return r;
+ }
+
+ printf("%c %c\n",
+ previous <= 0 ? 'N' : previous,
+ runlevel <= 0 ? 'N' : runlevel);
+
+ return 0;
+}
+
+int main(int argc, char*argv[]) {
+ int r, retval = EXIT_FAILURE;
+ DBusConnection *bus = NULL;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r < 0)
+ goto finish;
+ else if (r == 0) {
+ retval = EXIT_SUCCESS;
+ goto finish;
+ }
+
+ /* /sbin/runlevel doesn't need to communicate via D-Bus, so
+ * let's shortcut this */
+ if (arg_action == ACTION_RUNLEVEL) {
+ r = runlevel_main();
+ retval = r < 0 ? EXIT_FAILURE : r;
+ goto finish;
+ }
+
+ if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
+ log_info("Running in chroot, ignoring request.");
+ retval = 0;
+ goto finish;
+ }
+
+ if (!avoid_bus()) {
+ if (arg_transport == TRANSPORT_NORMAL)
+ bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
+ else if (arg_transport == TRANSPORT_POLKIT) {
+ bus_connect_system_polkit(&bus, &error);
+ private_bus = false;
+ } else if (arg_transport == TRANSPORT_SSH) {
+ bus_connect_system_ssh(NULL, arg_host, &bus, &error);
+ private_bus = false;
+ } else
+ assert_not_reached("Uh, invalid transport...");
+ }
+
+ switch (arg_action) {
+
+ case ACTION_SYSTEMCTL:
+ r = systemctl_main(bus, argc, argv, &error);
+ break;
+
+ case ACTION_HALT:
+ case ACTION_POWEROFF:
+ case ACTION_REBOOT:
+ case ACTION_KEXEC:
+ r = halt_main(bus);
+ break;
+
+ case ACTION_RUNLEVEL2:
+ case ACTION_RUNLEVEL3:
+ case ACTION_RUNLEVEL4:
+ case ACTION_RUNLEVEL5:
+ case ACTION_RESCUE:
+ case ACTION_EMERGENCY:
+ case ACTION_DEFAULT:
+ r = start_with_fallback(bus);
+ break;
+
+ case ACTION_RELOAD:
+ case ACTION_REEXEC:
+ r = reload_with_fallback(bus);
+ break;
+
+ case ACTION_CANCEL_SHUTDOWN:
+ r = send_shutdownd(0, 0, false, false, NULL);
+ break;
+
+ case ACTION_INVALID:
+ case ACTION_RUNLEVEL:
+ default:
+ assert_not_reached("Unknown action");
+ }
+
+ retval = r < 0 ? EXIT_FAILURE : r;
+
+finish:
+ if (bus) {
+ dbus_connection_flush(bus);
+ dbus_connection_close(bus);
+ dbus_connection_unref(bus);
+ }
+
+ dbus_error_free(&error);
+
+ dbus_shutdown();
+
+ strv_free(arg_property);
+
+ pager_close();
+ ask_password_agent_close();
+ polkit_agent_close();
+
+ return retval;
+}
diff --git a/src/timestamp.c b/src/timestamp.c
deleted file mode 100644
index 1152f1b..0000000
--- a/src/timestamp.c
+++ /dev/null
@@ -1,39 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-
-#include "util.h"
-
-int main(int argc, char *argv[]) {
- struct dual_timestamp t;
-
- /* This is mostly useful for stuff like init ram disk scripts
- * which want to take a proper timestamp to do minimal bootup
- * profiling. */
-
- dual_timestamp_get(&t);
- printf("%llu %llu\n",
- (unsigned long long) t.realtime,
- (unsigned long long) t.monotonic);
-
- return 0;
-}
diff --git a/src/timestamp/timestamp.c b/src/timestamp/timestamp.c
new file mode 100644
index 0000000..1152f1b
--- /dev/null
+++ b/src/timestamp/timestamp.c
@@ -0,0 +1,39 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ struct dual_timestamp t;
+
+ /* This is mostly useful for stuff like init ram disk scripts
+ * which want to take a proper timestamp to do minimal bootup
+ * profiling. */
+
+ dual_timestamp_get(&t);
+ printf("%llu %llu\n",
+ (unsigned long long) t.realtime,
+ (unsigned long long) t.monotonic);
+
+ return 0;
+}
diff --git a/src/tmpfiles.c b/src/tmpfiles.c
deleted file mode 100644
index 1591308..0000000
--- a/src/tmpfiles.c
+++ /dev/null
@@ -1,1315 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering, Kay Sievers
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <dirent.h>
-#include <grp.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <glob.h>
-#include <fnmatch.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "strv.h"
-#include "label.h"
-#include "set.h"
-
-/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
- * them in the file system. This is intended to be used to create
- * properly owned directories beneath /tmp, /var/tmp, /run, which are
- * volatile and hence need to be recreated on bootup. */
-
-typedef enum ItemType {
- /* These ones take file names */
- CREATE_FILE = 'f',
- TRUNCATE_FILE = 'F',
- WRITE_FILE = 'w',
- CREATE_DIRECTORY = 'd',
- TRUNCATE_DIRECTORY = 'D',
- CREATE_FIFO = 'p',
- CREATE_SYMLINK = 'L',
- CREATE_CHAR_DEVICE = 'c',
- CREATE_BLOCK_DEVICE = 'b',
-
- /* These ones take globs */
- IGNORE_PATH = 'x',
- REMOVE_PATH = 'r',
- RECURSIVE_REMOVE_PATH = 'R',
- RELABEL_PATH = 'z',
- RECURSIVE_RELABEL_PATH = 'Z'
-} ItemType;
-
-typedef struct Item {
- ItemType type;
-
- char *path;
- char *argument;
- uid_t uid;
- gid_t gid;
- mode_t mode;
- usec_t age;
-
- dev_t major_minor;
-
- bool uid_set:1;
- bool gid_set:1;
- bool mode_set:1;
- bool age_set:1;
-} Item;
-
-static Hashmap *items = NULL, *globs = NULL;
-static Set *unix_sockets = NULL;
-
-static bool arg_create = false;
-static bool arg_clean = false;
-static bool arg_remove = false;
-
-static const char *arg_prefix = NULL;
-
-#define MAX_DEPTH 256
-
-static bool needs_glob(ItemType t) {
- return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
-}
-
-static struct Item* find_glob(Hashmap *h, const char *match) {
- Item *j;
- Iterator i;
-
- HASHMAP_FOREACH(j, h, i)
- if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
- return j;
-
- return NULL;
-}
-
-static void load_unix_sockets(void) {
- FILE *f = NULL;
- char line[LINE_MAX];
-
- if (unix_sockets)
- return;
-
- /* We maintain a cache of the sockets we found in
- * /proc/net/unix to speed things up a little. */
-
- unix_sockets = set_new(string_hash_func, string_compare_func);
- if (!unix_sockets)
- return;
-
- f = fopen("/proc/net/unix", "re");
- if (!f)
- return;
-
- /* Skip header */
- if (!fgets(line, sizeof(line), f))
- goto fail;
-
- for (;;) {
- char *p, *s;
- int k;
-
- if (!fgets(line, sizeof(line), f))
- break;
-
- truncate_nl(line);
-
- p = strchr(line, ':');
- if (!p)
- continue;
-
- if (strlen(p) < 37)
- continue;
-
- p += 37;
- p += strspn(p, WHITESPACE);
- p += strcspn(p, WHITESPACE); /* skip one more word */
- p += strspn(p, WHITESPACE);
-
- if (*p != '/')
- continue;
-
- s = strdup(p);
- if (!s)
- goto fail;
-
- path_kill_slashes(s);
-
- k = set_put(unix_sockets, s);
- if (k < 0) {
- free(s);
-
- if (k != -EEXIST)
- goto fail;
- }
- }
-
- fclose(f);
- return;
-
-fail:
- set_free_free(unix_sockets);
- unix_sockets = NULL;
-
- if (f)
- fclose(f);
-}
-
-static bool unix_socket_alive(const char *fn) {
- assert(fn);
-
- load_unix_sockets();
-
- if (unix_sockets)
- return !!set_get(unix_sockets, (char*) fn);
-
- /* We don't know, so assume yes */
- return true;
-}
-
-static int dir_cleanup(
- const char *p,
- DIR *d,
- const struct stat *ds,
- usec_t cutoff,
- dev_t rootdev,
- bool mountpoint,
- int maxdepth)
-{
- struct dirent *dent;
- struct timespec times[2];
- bool deleted = false;
- char *sub_path = NULL;
- int r = 0;
-
- while ((dent = readdir(d))) {
- struct stat s;
- usec_t age;
-
- if (streq(dent->d_name, ".") ||
- streq(dent->d_name, ".."))
- continue;
-
- if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
-
- if (errno != ENOENT) {
- log_error("stat(%s/%s) failed: %m", p, dent->d_name);
- r = -errno;
- }
-
- continue;
- }
-
- /* Stay on the same filesystem */
- if (s.st_dev != rootdev)
- continue;
-
- /* Do not delete read-only files owned by root */
- if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
- continue;
-
- free(sub_path);
- sub_path = NULL;
-
- if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- /* Is there an item configured for this path? */
- if (hashmap_get(items, sub_path))
- continue;
-
- if (find_glob(globs, sub_path))
- continue;
-
- if (S_ISDIR(s.st_mode)) {
-
- if (mountpoint &&
- streq(dent->d_name, "lost+found") &&
- s.st_uid == 0)
- continue;
-
- if (maxdepth <= 0)
- log_warning("Reached max depth on %s.", sub_path);
- else {
- DIR *sub_dir;
- int q;
-
- sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
- if (sub_dir == NULL) {
- if (errno != ENOENT) {
- log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
- r = -errno;
- }
-
- continue;
- }
-
- q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
- closedir(sub_dir);
-
- if (q < 0)
- r = q;
- }
-
- /* Ignore ctime, we change it when deleting */
- age = MAX(timespec_load(&s.st_mtim),
- timespec_load(&s.st_atim));
- if (age >= cutoff)
- continue;
-
- log_debug("rmdir '%s'\n", sub_path);
-
- if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
- if (errno != ENOENT && errno != ENOTEMPTY) {
- log_error("rmdir(%s): %m", sub_path);
- r = -errno;
- }
- }
-
- } else {
- /* Skip files for which the sticky bit is
- * set. These are semantics we define, and are
- * unknown elsewhere. See XDG_RUNTIME_DIR
- * specification for details. */
- if (s.st_mode & S_ISVTX)
- continue;
-
- if (mountpoint && S_ISREG(s.st_mode)) {
- if (streq(dent->d_name, ".journal") &&
- s.st_uid == 0)
- continue;
-
- if (streq(dent->d_name, "aquota.user") ||
- streq(dent->d_name, "aquota.group"))
- continue;
- }
-
- /* Ignore sockets that are listed in /proc/net/unix */
- if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
- continue;
-
- /* Ignore device nodes */
- if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
- continue;
-
- age = MAX3(timespec_load(&s.st_mtim),
- timespec_load(&s.st_atim),
- timespec_load(&s.st_ctim));
-
- if (age >= cutoff)
- continue;
-
- log_debug("unlink '%s'\n", sub_path);
-
- if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
- if (errno != ENOENT) {
- log_error("unlink(%s): %m", sub_path);
- r = -errno;
- }
- }
-
- deleted = true;
- }
- }
-
-finish:
- if (deleted) {
- /* Restore original directory timestamps */
- times[0] = ds->st_atim;
- times[1] = ds->st_mtim;
-
- if (futimens(dirfd(d), times) < 0)
- log_error("utimensat(%s): %m", p);
- }
-
- free(sub_path);
-
- return r;
-}
-
-static int clean_item(Item *i) {
- DIR *d;
- struct stat s, ps;
- bool mountpoint;
- int r;
- usec_t cutoff, n;
-
- assert(i);
-
- if (i->type != CREATE_DIRECTORY &&
- i->type != TRUNCATE_DIRECTORY &&
- i->type != IGNORE_PATH)
- return 0;
-
- if (!i->age_set || i->age <= 0)
- return 0;
-
- n = now(CLOCK_REALTIME);
- if (n < i->age)
- return 0;
-
- cutoff = n - i->age;
-
- d = opendir(i->path);
- if (!d) {
- if (errno == ENOENT)
- return 0;
-
- log_error("Failed to open directory %s: %m", i->path);
- return -errno;
- }
-
- if (fstat(dirfd(d), &s) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- r = -errno;
- goto finish;
- }
-
- if (!S_ISDIR(s.st_mode)) {
- log_error("%s is not a directory.", i->path);
- r = -ENOTDIR;
- goto finish;
- }
-
- if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
- log_error("stat(%s/..) failed: %m", i->path);
- r = -errno;
- goto finish;
- }
-
- mountpoint = s.st_dev != ps.st_dev ||
- (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
-
- r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int item_set_perms(Item *i, const char *path) {
- /* not using i->path directly because it may be a glob */
- if (i->mode_set)
- if (chmod(path, i->mode) < 0) {
- log_error("chmod(%s) failed: %m", path);
- return -errno;
- }
-
- if (i->uid_set || i->gid_set)
- if (chown(path,
- i->uid_set ? i->uid : (uid_t) -1,
- i->gid_set ? i->gid : (gid_t) -1) < 0) {
-
- log_error("chown(%s) failed: %m", path);
- return -errno;
- }
-
- return label_fix(path, false);
-}
-
-static int recursive_relabel_children(Item *i, const char *path) {
- DIR *d;
- int ret = 0;
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on */
-
- d = opendir(path);
- if (!d)
- return errno == ENOENT ? 0 : -errno;
-
- for (;;) {
- struct dirent buf, *de;
- bool is_dir;
- int r;
- char *entry_path;
-
- r = readdir_r(d, &buf, &de);
- if (r != 0) {
- if (ret == 0)
- ret = -r;
- break;
- }
-
- if (!de)
- break;
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
- if (ret == 0)
- ret = -ENOMEM;
- continue;
- }
-
- if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
- if (lstat(entry_path, &st) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- free(entry_path);
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
-
- } else
- is_dir = de->d_type == DT_DIR;
-
- r = item_set_perms(i, entry_path);
- if (r < 0) {
- if (ret == 0 && r != -ENOENT)
- ret = r;
- free(entry_path);
- continue;
- }
-
- if (is_dir) {
- r = recursive_relabel_children(i, entry_path);
- if (r < 0 && ret == 0)
- ret = r;
- }
-
- free(entry_path);
- }
-
- closedir(d);
-
- return ret;
-}
-
-static int recursive_relabel(Item *i, const char *path) {
- int r;
- struct stat st;
-
- r = item_set_perms(i, path);
- if (r < 0)
- return r;
-
- if (lstat(path, &st) < 0)
- return -errno;
-
- if (S_ISDIR(st.st_mode))
- r = recursive_relabel_children(i, path);
-
- return r;
-}
-
-static int glob_item(Item *i, int (*action)(Item *, const char *)) {
- int r = 0, k;
- glob_t g;
- char **fn;
-
- zero(g);
-
- errno = 0;
- if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
-
- if (k != GLOB_NOMATCH) {
- if (errno != 0)
- errno = EIO;
-
- log_error("glob(%s) failed: %m", i->path);
- return -errno;
- }
- }
-
- STRV_FOREACH(fn, g.gl_pathv)
- if ((k = action(i, *fn)) < 0)
- r = k;
-
- globfree(&g);
- return r;
-}
-
-static int create_item(Item *i) {
- int r;
- mode_t u;
- struct stat st;
-
- assert(i);
-
- switch (i->type) {
-
- case IGNORE_PATH:
- case REMOVE_PATH:
- case RECURSIVE_REMOVE_PATH:
- return 0;
-
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case WRITE_FILE: {
- int fd, flags;
-
- flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
- i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
-
- u = umask(0);
- fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
- umask(u);
-
- if (fd < 0) {
- if (i->type == WRITE_FILE && errno == ENOENT)
- break;
-
- log_error("Failed to create file %s: %m", i->path);
- return -errno;
- }
-
- if (i->argument) {
- ssize_t n;
- size_t l;
- struct iovec iovec[2];
- static const char new_line = '\n';
-
- l = strlen(i->argument);
-
- zero(iovec);
- iovec[0].iov_base = i->argument;
- iovec[0].iov_len = l;
-
- iovec[1].iov_base = (void*) &new_line;
- iovec[1].iov_len = 1;
-
- n = writev(fd, iovec, 2);
- if (n < 0 || (size_t) n != l+1) {
- log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
- close_nointr_nofail(fd);
- return n < 0 ? n : -EIO;
- }
- }
-
- close_nointr_nofail(fd);
-
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
-
- if (!S_ISREG(st.st_mode)) {
- log_error("%s is not a file.", i->path);
- return -EEXIST;
- }
-
- r = item_set_perms(i, i->path);
- if (r < 0)
- return r;
-
- break;
- }
-
- case TRUNCATE_DIRECTORY:
- case CREATE_DIRECTORY:
-
- u = umask(0);
- mkdir_parents(i->path, 0755);
- r = mkdir(i->path, i->mode);
- umask(u);
-
- if (r < 0 && errno != EEXIST) {
- log_error("Failed to create directory %s: %m", i->path);
- return -errno;
- }
-
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- log_error("%s is not a directory.", i->path);
- return -EEXIST;
- }
-
- r = item_set_perms(i, i->path);
- if (r < 0)
- return r;
-
- break;
-
- case CREATE_FIFO:
-
- u = umask(0);
- r = mkfifo(i->path, i->mode);
- umask(u);
-
- if (r < 0 && errno != EEXIST) {
- log_error("Failed to create fifo %s: %m", i->path);
- return -errno;
- }
-
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
-
- if (!S_ISFIFO(st.st_mode)) {
- log_error("%s is not a fifo.", i->path);
- return -EEXIST;
- }
-
- r = item_set_perms(i, i->path);
- if (r < 0)
- return r;
-
- break;
-
- case CREATE_SYMLINK: {
- char *x;
-
- r = symlink(i->argument, i->path);
- if (r < 0 && errno != EEXIST) {
- log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
- return -errno;
- }
-
- r = readlink_malloc(i->path, &x);
- if (r < 0) {
- log_error("readlink(%s) failed: %s", i->path, strerror(-r));
- return -errno;
- }
-
- if (!streq(i->argument, x)) {
- free(x);
- log_error("%s is not the right symlinks.", i->path);
- return -EEXIST;
- }
-
- free(x);
- break;
- }
-
- case CREATE_BLOCK_DEVICE:
- case CREATE_CHAR_DEVICE: {
-
- u = umask(0);
- r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
- umask(u);
-
- if (r < 0 && errno != EEXIST) {
- log_error("Failed to create device node %s: %m", i->path);
- return -errno;
- }
-
- if (stat(i->path, &st) < 0) {
- log_error("stat(%s) failed: %m", i->path);
- return -errno;
- }
-
- if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
- log_error("%s is not a device node.", i->path);
- return -EEXIST;
- }
-
- r = item_set_perms(i, i->path);
- if (r < 0)
- return r;
-
- break;
- }
-
- case RELABEL_PATH:
-
- r = glob_item(i, item_set_perms);
- if (r < 0)
- return 0;
- break;
-
- case RECURSIVE_RELABEL_PATH:
-
- r = glob_item(i, recursive_relabel);
- if (r < 0)
- return r;
- }
-
- log_debug("%s created successfully.", i->path);
-
- return 0;
-}
-
-static int remove_item_instance(Item *i, const char *instance) {
- int r;
-
- assert(i);
-
- switch (i->type) {
-
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case CREATE_FIFO:
- case CREATE_SYMLINK:
- case CREATE_BLOCK_DEVICE:
- case CREATE_CHAR_DEVICE:
- case IGNORE_PATH:
- case RELABEL_PATH:
- case RECURSIVE_RELABEL_PATH:
- case WRITE_FILE:
- break;
-
- case REMOVE_PATH:
- if (remove(instance) < 0 && errno != ENOENT) {
- log_error("remove(%s): %m", instance);
- return -errno;
- }
-
- break;
-
- case TRUNCATE_DIRECTORY:
- case RECURSIVE_REMOVE_PATH:
- r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
- if (r < 0 && r != -ENOENT) {
- log_error("rm_rf(%s): %s", instance, strerror(-r));
- return r;
- }
-
- break;
- }
-
- return 0;
-}
-
-static int remove_item(Item *i) {
- int r = 0;
-
- assert(i);
-
- switch (i->type) {
-
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case CREATE_FIFO:
- case CREATE_SYMLINK:
- case CREATE_CHAR_DEVICE:
- case CREATE_BLOCK_DEVICE:
- case IGNORE_PATH:
- case RELABEL_PATH:
- case RECURSIVE_RELABEL_PATH:
- case WRITE_FILE:
- break;
-
- case REMOVE_PATH:
- case TRUNCATE_DIRECTORY:
- case RECURSIVE_REMOVE_PATH:
- r = glob_item(i, remove_item_instance);
- break;
- }
-
- return r;
-}
-
-static int process_item(Item *i) {
- int r, q, p;
-
- assert(i);
-
- r = arg_create ? create_item(i) : 0;
- q = arg_remove ? remove_item(i) : 0;
- p = arg_clean ? clean_item(i) : 0;
-
- if (r < 0)
- return r;
-
- if (q < 0)
- return q;
-
- return p;
-}
-
-static void item_free(Item *i) {
- assert(i);
-
- free(i->path);
- free(i->argument);
- free(i);
-}
-
-static bool item_equal(Item *a, Item *b) {
- assert(a);
- assert(b);
-
- if (!streq_ptr(a->path, b->path))
- return false;
-
- if (a->type != b->type)
- return false;
-
- if (a->uid_set != b->uid_set ||
- (a->uid_set && a->uid != b->uid))
- return false;
-
- if (a->gid_set != b->gid_set ||
- (a->gid_set && a->gid != b->gid))
- return false;
-
- if (a->mode_set != b->mode_set ||
- (a->mode_set && a->mode != b->mode))
- return false;
-
- if (a->age_set != b->age_set ||
- (a->age_set && a->age != b->age))
- return false;
-
- if ((a->type == CREATE_FILE ||
- a->type == TRUNCATE_FILE ||
- a->type == WRITE_FILE ||
- a->type == CREATE_SYMLINK) &&
- !streq_ptr(a->argument, b->argument))
- return false;
-
- if ((a->type == CREATE_CHAR_DEVICE ||
- a->type == CREATE_BLOCK_DEVICE) &&
- a->major_minor != b->major_minor)
- return false;
-
- return true;
-}
-
-static int parse_line(const char *fname, unsigned line, const char *buffer) {
- Item *i, *existing;
- char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
- char type;
- Hashmap *h;
- int r, n = -1;
-
- assert(fname);
- assert(line >= 1);
- assert(buffer);
-
- i = new0(Item, 1);
- if (!i) {
- log_error("Out of memory");
- return -ENOMEM;
- }
-
- if (sscanf(buffer,
- "%c "
- "%ms "
- "%ms "
- "%ms "
- "%ms "
- "%ms "
- "%n",
- &type,
- &i->path,
- &mode,
- &user,
- &group,
- &age,
- &n) < 2) {
- log_error("[%s:%u] Syntax error.", fname, line);
- r = -EIO;
- goto finish;
- }
-
- if (n >= 0) {
- n += strspn(buffer+n, WHITESPACE);
- if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
- i->argument = unquote(buffer+n, "\"");
- if (!i->argument) {
- log_error("Out of memory");
- return -ENOMEM;
- }
- }
- }
-
- switch(type) {
-
- case CREATE_FILE:
- case TRUNCATE_FILE:
- case CREATE_DIRECTORY:
- case TRUNCATE_DIRECTORY:
- case CREATE_FIFO:
- case IGNORE_PATH:
- case REMOVE_PATH:
- case RECURSIVE_REMOVE_PATH:
- case RELABEL_PATH:
- case RECURSIVE_RELABEL_PATH:
- break;
-
- case CREATE_SYMLINK:
- if (!i->argument) {
- log_error("[%s:%u] Symlink file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
- }
- break;
-
- case WRITE_FILE:
- if (!i->argument) {
- log_error("[%s:%u] Write file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
- }
- break;
-
- case CREATE_CHAR_DEVICE:
- case CREATE_BLOCK_DEVICE: {
- unsigned major, minor;
-
- if (!i->argument) {
- log_error("[%s:%u] Device file requires argument.", fname, line);
- r = -EBADMSG;
- goto finish;
- }
-
- if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
- log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
- r = -EBADMSG;
- goto finish;
- }
-
- i->major_minor = makedev(major, minor);
- break;
- }
-
- default:
- log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
- r = -EBADMSG;
- goto finish;
- }
-
- i->type = type;
-
- if (!path_is_absolute(i->path)) {
- log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
- r = -EBADMSG;
- goto finish;
- }
-
- path_kill_slashes(i->path);
-
- if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
- r = 0;
- goto finish;
- }
-
- if (user && !streq(user, "-")) {
- const char *u = user;
-
- r = get_user_creds(&u, &i->uid, NULL, NULL);
- if (r < 0) {
- log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
- goto finish;
- }
-
- i->uid_set = true;
- }
-
- if (group && !streq(group, "-")) {
- const char *g = group;
-
- r = get_group_creds(&g, &i->gid);
- if (r < 0) {
- log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
- goto finish;
- }
-
- i->gid_set = true;
- }
-
- if (mode && !streq(mode, "-")) {
- unsigned m;
-
- if (sscanf(mode, "%o", &m) != 1) {
- log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
- r = -ENOENT;
- goto finish;
- }
-
- i->mode = m;
- i->mode_set = true;
- } else
- i->mode =
- i->type == CREATE_DIRECTORY ||
- i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
-
- if (age && !streq(age, "-")) {
- if (parse_usec(age, &i->age) < 0) {
- log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
- r = -EBADMSG;
- goto finish;
- }
-
- i->age_set = true;
- }
-
- h = needs_glob(i->type) ? globs : items;
-
- existing = hashmap_get(h, i->path);
- if (existing) {
-
- /* Two identical items are fine */
- if (!item_equal(existing, i))
- log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
-
- r = 0;
- goto finish;
- }
-
- r = hashmap_put(h, i->path, i);
- if (r < 0) {
- log_error("Failed to insert item %s: %s", i->path, strerror(-r));
- goto finish;
- }
-
- i = NULL;
- r = 0;
-
-finish:
- free(user);
- free(group);
- free(mode);
- free(age);
-
- if (i)
- item_free(i);
-
- return r;
-}
-
-static int help(void) {
-
- printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
- "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
- " -h --help Show this help\n"
- " --create Create marked files/directories\n"
- " --clean Clean up marked directories\n"
- " --remove Remove marked files/directories\n"
- " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_CREATE,
- ARG_CLEAN,
- ARG_REMOVE,
- ARG_PREFIX
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "create", no_argument, NULL, ARG_CREATE },
- { "clean", no_argument, NULL, ARG_CLEAN },
- { "remove", no_argument, NULL, ARG_REMOVE },
- { "prefix", required_argument, NULL, ARG_PREFIX },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_CREATE:
- arg_create = true;
- break;
-
- case ARG_CLEAN:
- arg_clean = true;
- break;
-
- case ARG_REMOVE:
- arg_remove = true;
- break;
-
- case ARG_PREFIX:
- arg_prefix = optarg;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (!arg_clean && !arg_create && !arg_remove) {
- log_error("You need to specify at least one of --clean, --create or --remove.");
- return -EINVAL;
- }
-
- return 1;
-}
-
-static int read_config_file(const char *fn, bool ignore_enoent) {
- FILE *f;
- unsigned v = 0;
- int r = 0;
-
- assert(fn);
-
- f = fopen(fn, "re");
- if (!f) {
-
- if (ignore_enoent && errno == ENOENT)
- return 0;
-
- log_error("Failed to open %s: %m", fn);
- return -errno;
- }
-
- log_debug("apply: %s\n", fn);
- for (;;) {
- char line[LINE_MAX], *l;
- int k;
-
- if (!(fgets(line, sizeof(line), f)))
- break;
-
- v++;
-
- l = strstrip(line);
- if (*l == '#' || *l == 0)
- continue;
-
- if ((k = parse_line(fn, v, l)) < 0)
- if (r == 0)
- r = k;
- }
-
- if (ferror(f)) {
- log_error("Failed to read from file %s: %m", fn);
- if (r == 0)
- r = -EIO;
- }
-
- fclose(f);
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- int r;
- Item *i;
- Iterator iterator;
-
- r = parse_argv(argc, argv);
- if (r <= 0)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- label_init();
-
- items = hashmap_new(string_hash_func, string_compare_func);
- globs = hashmap_new(string_hash_func, string_compare_func);
-
- if (!items || !globs) {
- log_error("Out of memory");
- r = EXIT_FAILURE;
- goto finish;
- }
-
- r = EXIT_SUCCESS;
-
- if (optind < argc) {
- int j;
-
- for (j = optind; j < argc; j++)
- if (read_config_file(argv[j], false) < 0)
- r = EXIT_FAILURE;
-
- } else {
- char **files, **f;
-
- r = conf_files_list(&files, ".conf",
- "/etc/tmpfiles.d",
- "/run/tmpfiles.d",
- "/usr/local/lib/tmpfiles.d",
- "/usr/lib/tmpfiles.d",
- NULL);
- if (r < 0) {
- r = EXIT_FAILURE;
- log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
- goto finish;
- }
-
- STRV_FOREACH(f, files) {
- if (read_config_file(*f, true) < 0)
- r = EXIT_FAILURE;
- }
-
- strv_free(files);
- }
-
- HASHMAP_FOREACH(i, globs, iterator)
- process_item(i);
-
- HASHMAP_FOREACH(i, items, iterator)
- process_item(i);
-
-finish:
- while ((i = hashmap_steal_first(items)))
- item_free(i);
-
- while ((i = hashmap_steal_first(globs)))
- item_free(i);
-
- hashmap_free(items);
- hashmap_free(globs);
-
- set_free_free(unix_sockets);
-
- label_finish();
-
- return r;
-}
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
new file mode 100644
index 0000000..1591308
--- /dev/null
+++ b/src/tmpfiles/tmpfiles.c
@@ -0,0 +1,1315 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering, Kay Sievers
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <glob.h>
+#include <fnmatch.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "strv.h"
+#include "label.h"
+#include "set.h"
+
+/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
+ * them in the file system. This is intended to be used to create
+ * properly owned directories beneath /tmp, /var/tmp, /run, which are
+ * volatile and hence need to be recreated on bootup. */
+
+typedef enum ItemType {
+ /* These ones take file names */
+ CREATE_FILE = 'f',
+ TRUNCATE_FILE = 'F',
+ WRITE_FILE = 'w',
+ CREATE_DIRECTORY = 'd',
+ TRUNCATE_DIRECTORY = 'D',
+ CREATE_FIFO = 'p',
+ CREATE_SYMLINK = 'L',
+ CREATE_CHAR_DEVICE = 'c',
+ CREATE_BLOCK_DEVICE = 'b',
+
+ /* These ones take globs */
+ IGNORE_PATH = 'x',
+ REMOVE_PATH = 'r',
+ RECURSIVE_REMOVE_PATH = 'R',
+ RELABEL_PATH = 'z',
+ RECURSIVE_RELABEL_PATH = 'Z'
+} ItemType;
+
+typedef struct Item {
+ ItemType type;
+
+ char *path;
+ char *argument;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ usec_t age;
+
+ dev_t major_minor;
+
+ bool uid_set:1;
+ bool gid_set:1;
+ bool mode_set:1;
+ bool age_set:1;
+} Item;
+
+static Hashmap *items = NULL, *globs = NULL;
+static Set *unix_sockets = NULL;
+
+static bool arg_create = false;
+static bool arg_clean = false;
+static bool arg_remove = false;
+
+static const char *arg_prefix = NULL;
+
+#define MAX_DEPTH 256
+
+static bool needs_glob(ItemType t) {
+ return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
+}
+
+static struct Item* find_glob(Hashmap *h, const char *match) {
+ Item *j;
+ Iterator i;
+
+ HASHMAP_FOREACH(j, h, i)
+ if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
+ return j;
+
+ return NULL;
+}
+
+static void load_unix_sockets(void) {
+ FILE *f = NULL;
+ char line[LINE_MAX];
+
+ if (unix_sockets)
+ return;
+
+ /* We maintain a cache of the sockets we found in
+ * /proc/net/unix to speed things up a little. */
+
+ unix_sockets = set_new(string_hash_func, string_compare_func);
+ if (!unix_sockets)
+ return;
+
+ f = fopen("/proc/net/unix", "re");
+ if (!f)
+ return;
+
+ /* Skip header */
+ if (!fgets(line, sizeof(line), f))
+ goto fail;
+
+ for (;;) {
+ char *p, *s;
+ int k;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ truncate_nl(line);
+
+ p = strchr(line, ':');
+ if (!p)
+ continue;
+
+ if (strlen(p) < 37)
+ continue;
+
+ p += 37;
+ p += strspn(p, WHITESPACE);
+ p += strcspn(p, WHITESPACE); /* skip one more word */
+ p += strspn(p, WHITESPACE);
+
+ if (*p != '/')
+ continue;
+
+ s = strdup(p);
+ if (!s)
+ goto fail;
+
+ path_kill_slashes(s);
+
+ k = set_put(unix_sockets, s);
+ if (k < 0) {
+ free(s);
+
+ if (k != -EEXIST)
+ goto fail;
+ }
+ }
+
+ fclose(f);
+ return;
+
+fail:
+ set_free_free(unix_sockets);
+ unix_sockets = NULL;
+
+ if (f)
+ fclose(f);
+}
+
+static bool unix_socket_alive(const char *fn) {
+ assert(fn);
+
+ load_unix_sockets();
+
+ if (unix_sockets)
+ return !!set_get(unix_sockets, (char*) fn);
+
+ /* We don't know, so assume yes */
+ return true;
+}
+
+static int dir_cleanup(
+ const char *p,
+ DIR *d,
+ const struct stat *ds,
+ usec_t cutoff,
+ dev_t rootdev,
+ bool mountpoint,
+ int maxdepth)
+{
+ struct dirent *dent;
+ struct timespec times[2];
+ bool deleted = false;
+ char *sub_path = NULL;
+ int r = 0;
+
+ while ((dent = readdir(d))) {
+ struct stat s;
+ usec_t age;
+
+ if (streq(dent->d_name, ".") ||
+ streq(dent->d_name, ".."))
+ continue;
+
+ if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
+
+ if (errno != ENOENT) {
+ log_error("stat(%s/%s) failed: %m", p, dent->d_name);
+ r = -errno;
+ }
+
+ continue;
+ }
+
+ /* Stay on the same filesystem */
+ if (s.st_dev != rootdev)
+ continue;
+
+ /* Do not delete read-only files owned by root */
+ if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
+ continue;
+
+ free(sub_path);
+ sub_path = NULL;
+
+ if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ /* Is there an item configured for this path? */
+ if (hashmap_get(items, sub_path))
+ continue;
+
+ if (find_glob(globs, sub_path))
+ continue;
+
+ if (S_ISDIR(s.st_mode)) {
+
+ if (mountpoint &&
+ streq(dent->d_name, "lost+found") &&
+ s.st_uid == 0)
+ continue;
+
+ if (maxdepth <= 0)
+ log_warning("Reached max depth on %s.", sub_path);
+ else {
+ DIR *sub_dir;
+ int q;
+
+ sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
+ if (sub_dir == NULL) {
+ if (errno != ENOENT) {
+ log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
+ r = -errno;
+ }
+
+ continue;
+ }
+
+ q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
+ closedir(sub_dir);
+
+ if (q < 0)
+ r = q;
+ }
+
+ /* Ignore ctime, we change it when deleting */
+ age = MAX(timespec_load(&s.st_mtim),
+ timespec_load(&s.st_atim));
+ if (age >= cutoff)
+ continue;
+
+ log_debug("rmdir '%s'\n", sub_path);
+
+ if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
+ if (errno != ENOENT && errno != ENOTEMPTY) {
+ log_error("rmdir(%s): %m", sub_path);
+ r = -errno;
+ }
+ }
+
+ } else {
+ /* Skip files for which the sticky bit is
+ * set. These are semantics we define, and are
+ * unknown elsewhere. See XDG_RUNTIME_DIR
+ * specification for details. */
+ if (s.st_mode & S_ISVTX)
+ continue;
+
+ if (mountpoint && S_ISREG(s.st_mode)) {
+ if (streq(dent->d_name, ".journal") &&
+ s.st_uid == 0)
+ continue;
+
+ if (streq(dent->d_name, "aquota.user") ||
+ streq(dent->d_name, "aquota.group"))
+ continue;
+ }
+
+ /* Ignore sockets that are listed in /proc/net/unix */
+ if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
+ continue;
+
+ /* Ignore device nodes */
+ if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
+ continue;
+
+ age = MAX3(timespec_load(&s.st_mtim),
+ timespec_load(&s.st_atim),
+ timespec_load(&s.st_ctim));
+
+ if (age >= cutoff)
+ continue;
+
+ log_debug("unlink '%s'\n", sub_path);
+
+ if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
+ if (errno != ENOENT) {
+ log_error("unlink(%s): %m", sub_path);
+ r = -errno;
+ }
+ }
+
+ deleted = true;
+ }
+ }
+
+finish:
+ if (deleted) {
+ /* Restore original directory timestamps */
+ times[0] = ds->st_atim;
+ times[1] = ds->st_mtim;
+
+ if (futimens(dirfd(d), times) < 0)
+ log_error("utimensat(%s): %m", p);
+ }
+
+ free(sub_path);
+
+ return r;
+}
+
+static int clean_item(Item *i) {
+ DIR *d;
+ struct stat s, ps;
+ bool mountpoint;
+ int r;
+ usec_t cutoff, n;
+
+ assert(i);
+
+ if (i->type != CREATE_DIRECTORY &&
+ i->type != TRUNCATE_DIRECTORY &&
+ i->type != IGNORE_PATH)
+ return 0;
+
+ if (!i->age_set || i->age <= 0)
+ return 0;
+
+ n = now(CLOCK_REALTIME);
+ if (n < i->age)
+ return 0;
+
+ cutoff = n - i->age;
+
+ d = opendir(i->path);
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open directory %s: %m", i->path);
+ return -errno;
+ }
+
+ if (fstat(dirfd(d), &s) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ r = -errno;
+ goto finish;
+ }
+
+ if (!S_ISDIR(s.st_mode)) {
+ log_error("%s is not a directory.", i->path);
+ r = -ENOTDIR;
+ goto finish;
+ }
+
+ if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
+ log_error("stat(%s/..) failed: %m", i->path);
+ r = -errno;
+ goto finish;
+ }
+
+ mountpoint = s.st_dev != ps.st_dev ||
+ (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
+
+ r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+static int item_set_perms(Item *i, const char *path) {
+ /* not using i->path directly because it may be a glob */
+ if (i->mode_set)
+ if (chmod(path, i->mode) < 0) {
+ log_error("chmod(%s) failed: %m", path);
+ return -errno;
+ }
+
+ if (i->uid_set || i->gid_set)
+ if (chown(path,
+ i->uid_set ? i->uid : (uid_t) -1,
+ i->gid_set ? i->gid : (gid_t) -1) < 0) {
+
+ log_error("chown(%s) failed: %m", path);
+ return -errno;
+ }
+
+ return label_fix(path, false);
+}
+
+static int recursive_relabel_children(Item *i, const char *path) {
+ DIR *d;
+ int ret = 0;
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on */
+
+ d = opendir(path);
+ if (!d)
+ return errno == ENOENT ? 0 : -errno;
+
+ for (;;) {
+ struct dirent buf, *de;
+ bool is_dir;
+ int r;
+ char *entry_path;
+
+ r = readdir_r(d, &buf, &de);
+ if (r != 0) {
+ if (ret == 0)
+ ret = -r;
+ break;
+ }
+
+ if (!de)
+ break;
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
+ if (ret == 0)
+ ret = -ENOMEM;
+ continue;
+ }
+
+ if (de->d_type == DT_UNKNOWN) {
+ struct stat st;
+
+ if (lstat(entry_path, &st) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ free(entry_path);
+ continue;
+ }
+
+ is_dir = S_ISDIR(st.st_mode);
+
+ } else
+ is_dir = de->d_type == DT_DIR;
+
+ r = item_set_perms(i, entry_path);
+ if (r < 0) {
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
+ free(entry_path);
+ continue;
+ }
+
+ if (is_dir) {
+ r = recursive_relabel_children(i, entry_path);
+ if (r < 0 && ret == 0)
+ ret = r;
+ }
+
+ free(entry_path);
+ }
+
+ closedir(d);
+
+ return ret;
+}
+
+static int recursive_relabel(Item *i, const char *path) {
+ int r;
+ struct stat st;
+
+ r = item_set_perms(i, path);
+ if (r < 0)
+ return r;
+
+ if (lstat(path, &st) < 0)
+ return -errno;
+
+ if (S_ISDIR(st.st_mode))
+ r = recursive_relabel_children(i, path);
+
+ return r;
+}
+
+static int glob_item(Item *i, int (*action)(Item *, const char *)) {
+ int r = 0, k;
+ glob_t g;
+ char **fn;
+
+ zero(g);
+
+ errno = 0;
+ if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
+
+ if (k != GLOB_NOMATCH) {
+ if (errno != 0)
+ errno = EIO;
+
+ log_error("glob(%s) failed: %m", i->path);
+ return -errno;
+ }
+ }
+
+ STRV_FOREACH(fn, g.gl_pathv)
+ if ((k = action(i, *fn)) < 0)
+ r = k;
+
+ globfree(&g);
+ return r;
+}
+
+static int create_item(Item *i) {
+ int r;
+ mode_t u;
+ struct stat st;
+
+ assert(i);
+
+ switch (i->type) {
+
+ case IGNORE_PATH:
+ case REMOVE_PATH:
+ case RECURSIVE_REMOVE_PATH:
+ return 0;
+
+ case CREATE_FILE:
+ case TRUNCATE_FILE:
+ case WRITE_FILE: {
+ int fd, flags;
+
+ flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
+ i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
+
+ u = umask(0);
+ fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
+ umask(u);
+
+ if (fd < 0) {
+ if (i->type == WRITE_FILE && errno == ENOENT)
+ break;
+
+ log_error("Failed to create file %s: %m", i->path);
+ return -errno;
+ }
+
+ if (i->argument) {
+ ssize_t n;
+ size_t l;
+ struct iovec iovec[2];
+ static const char new_line = '\n';
+
+ l = strlen(i->argument);
+
+ zero(iovec);
+ iovec[0].iov_base = i->argument;
+ iovec[0].iov_len = l;
+
+ iovec[1].iov_base = (void*) &new_line;
+ iovec[1].iov_len = 1;
+
+ n = writev(fd, iovec, 2);
+ if (n < 0 || (size_t) n != l+1) {
+ log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
+ close_nointr_nofail(fd);
+ return n < 0 ? n : -EIO;
+ }
+ }
+
+ close_nointr_nofail(fd);
+
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ log_error("%s is not a file.", i->path);
+ return -EEXIST;
+ }
+
+ r = item_set_perms(i, i->path);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case TRUNCATE_DIRECTORY:
+ case CREATE_DIRECTORY:
+
+ u = umask(0);
+ mkdir_parents(i->path, 0755);
+ r = mkdir(i->path, i->mode);
+ umask(u);
+
+ if (r < 0 && errno != EEXIST) {
+ log_error("Failed to create directory %s: %m", i->path);
+ return -errno;
+ }
+
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ log_error("%s is not a directory.", i->path);
+ return -EEXIST;
+ }
+
+ r = item_set_perms(i, i->path);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case CREATE_FIFO:
+
+ u = umask(0);
+ r = mkfifo(i->path, i->mode);
+ umask(u);
+
+ if (r < 0 && errno != EEXIST) {
+ log_error("Failed to create fifo %s: %m", i->path);
+ return -errno;
+ }
+
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if (!S_ISFIFO(st.st_mode)) {
+ log_error("%s is not a fifo.", i->path);
+ return -EEXIST;
+ }
+
+ r = item_set_perms(i, i->path);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case CREATE_SYMLINK: {
+ char *x;
+
+ r = symlink(i->argument, i->path);
+ if (r < 0 && errno != EEXIST) {
+ log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
+ return -errno;
+ }
+
+ r = readlink_malloc(i->path, &x);
+ if (r < 0) {
+ log_error("readlink(%s) failed: %s", i->path, strerror(-r));
+ return -errno;
+ }
+
+ if (!streq(i->argument, x)) {
+ free(x);
+ log_error("%s is not the right symlinks.", i->path);
+ return -EEXIST;
+ }
+
+ free(x);
+ break;
+ }
+
+ case CREATE_BLOCK_DEVICE:
+ case CREATE_CHAR_DEVICE: {
+
+ u = umask(0);
+ r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
+ umask(u);
+
+ if (r < 0 && errno != EEXIST) {
+ log_error("Failed to create device node %s: %m", i->path);
+ return -errno;
+ }
+
+ if (stat(i->path, &st) < 0) {
+ log_error("stat(%s) failed: %m", i->path);
+ return -errno;
+ }
+
+ if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
+ log_error("%s is not a device node.", i->path);
+ return -EEXIST;
+ }
+
+ r = item_set_perms(i, i->path);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case RELABEL_PATH:
+
+ r = glob_item(i, item_set_perms);
+ if (r < 0)
+ return 0;
+ break;
+
+ case RECURSIVE_RELABEL_PATH:
+
+ r = glob_item(i, recursive_relabel);
+ if (r < 0)
+ return r;
+ }
+
+ log_debug("%s created successfully.", i->path);
+
+ return 0;
+}
+
+static int remove_item_instance(Item *i, const char *instance) {
+ int r;
+
+ assert(i);
+
+ switch (i->type) {
+
+ case CREATE_FILE:
+ case TRUNCATE_FILE:
+ case CREATE_DIRECTORY:
+ case CREATE_FIFO:
+ case CREATE_SYMLINK:
+ case CREATE_BLOCK_DEVICE:
+ case CREATE_CHAR_DEVICE:
+ case IGNORE_PATH:
+ case RELABEL_PATH:
+ case RECURSIVE_RELABEL_PATH:
+ case WRITE_FILE:
+ break;
+
+ case REMOVE_PATH:
+ if (remove(instance) < 0 && errno != ENOENT) {
+ log_error("remove(%s): %m", instance);
+ return -errno;
+ }
+
+ break;
+
+ case TRUNCATE_DIRECTORY:
+ case RECURSIVE_REMOVE_PATH:
+ r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
+ if (r < 0 && r != -ENOENT) {
+ log_error("rm_rf(%s): %s", instance, strerror(-r));
+ return r;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+static int remove_item(Item *i) {
+ int r = 0;
+
+ assert(i);
+
+ switch (i->type) {
+
+ case CREATE_FILE:
+ case TRUNCATE_FILE:
+ case CREATE_DIRECTORY:
+ case CREATE_FIFO:
+ case CREATE_SYMLINK:
+ case CREATE_CHAR_DEVICE:
+ case CREATE_BLOCK_DEVICE:
+ case IGNORE_PATH:
+ case RELABEL_PATH:
+ case RECURSIVE_RELABEL_PATH:
+ case WRITE_FILE:
+ break;
+
+ case REMOVE_PATH:
+ case TRUNCATE_DIRECTORY:
+ case RECURSIVE_REMOVE_PATH:
+ r = glob_item(i, remove_item_instance);
+ break;
+ }
+
+ return r;
+}
+
+static int process_item(Item *i) {
+ int r, q, p;
+
+ assert(i);
+
+ r = arg_create ? create_item(i) : 0;
+ q = arg_remove ? remove_item(i) : 0;
+ p = arg_clean ? clean_item(i) : 0;
+
+ if (r < 0)
+ return r;
+
+ if (q < 0)
+ return q;
+
+ return p;
+}
+
+static void item_free(Item *i) {
+ assert(i);
+
+ free(i->path);
+ free(i->argument);
+ free(i);
+}
+
+static bool item_equal(Item *a, Item *b) {
+ assert(a);
+ assert(b);
+
+ if (!streq_ptr(a->path, b->path))
+ return false;
+
+ if (a->type != b->type)
+ return false;
+
+ if (a->uid_set != b->uid_set ||
+ (a->uid_set && a->uid != b->uid))
+ return false;
+
+ if (a->gid_set != b->gid_set ||
+ (a->gid_set && a->gid != b->gid))
+ return false;
+
+ if (a->mode_set != b->mode_set ||
+ (a->mode_set && a->mode != b->mode))
+ return false;
+
+ if (a->age_set != b->age_set ||
+ (a->age_set && a->age != b->age))
+ return false;
+
+ if ((a->type == CREATE_FILE ||
+ a->type == TRUNCATE_FILE ||
+ a->type == WRITE_FILE ||
+ a->type == CREATE_SYMLINK) &&
+ !streq_ptr(a->argument, b->argument))
+ return false;
+
+ if ((a->type == CREATE_CHAR_DEVICE ||
+ a->type == CREATE_BLOCK_DEVICE) &&
+ a->major_minor != b->major_minor)
+ return false;
+
+ return true;
+}
+
+static int parse_line(const char *fname, unsigned line, const char *buffer) {
+ Item *i, *existing;
+ char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
+ char type;
+ Hashmap *h;
+ int r, n = -1;
+
+ assert(fname);
+ assert(line >= 1);
+ assert(buffer);
+
+ i = new0(Item, 1);
+ if (!i) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ if (sscanf(buffer,
+ "%c "
+ "%ms "
+ "%ms "
+ "%ms "
+ "%ms "
+ "%ms "
+ "%n",
+ &type,
+ &i->path,
+ &mode,
+ &user,
+ &group,
+ &age,
+ &n) < 2) {
+ log_error("[%s:%u] Syntax error.", fname, line);
+ r = -EIO;
+ goto finish;
+ }
+
+ if (n >= 0) {
+ n += strspn(buffer+n, WHITESPACE);
+ if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
+ i->argument = unquote(buffer+n, "\"");
+ if (!i->argument) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+ }
+ }
+
+ switch(type) {
+
+ case CREATE_FILE:
+ case TRUNCATE_FILE:
+ case CREATE_DIRECTORY:
+ case TRUNCATE_DIRECTORY:
+ case CREATE_FIFO:
+ case IGNORE_PATH:
+ case REMOVE_PATH:
+ case RECURSIVE_REMOVE_PATH:
+ case RELABEL_PATH:
+ case RECURSIVE_RELABEL_PATH:
+ break;
+
+ case CREATE_SYMLINK:
+ if (!i->argument) {
+ log_error("[%s:%u] Symlink file requires argument.", fname, line);
+ r = -EBADMSG;
+ goto finish;
+ }
+ break;
+
+ case WRITE_FILE:
+ if (!i->argument) {
+ log_error("[%s:%u] Write file requires argument.", fname, line);
+ r = -EBADMSG;
+ goto finish;
+ }
+ break;
+
+ case CREATE_CHAR_DEVICE:
+ case CREATE_BLOCK_DEVICE: {
+ unsigned major, minor;
+
+ if (!i->argument) {
+ log_error("[%s:%u] Device file requires argument.", fname, line);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
+ log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ i->major_minor = makedev(major, minor);
+ break;
+ }
+
+ default:
+ log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ i->type = type;
+
+ if (!path_is_absolute(i->path)) {
+ log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ path_kill_slashes(i->path);
+
+ if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
+ r = 0;
+ goto finish;
+ }
+
+ if (user && !streq(user, "-")) {
+ const char *u = user;
+
+ r = get_user_creds(&u, &i->uid, NULL, NULL);
+ if (r < 0) {
+ log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
+ goto finish;
+ }
+
+ i->uid_set = true;
+ }
+
+ if (group && !streq(group, "-")) {
+ const char *g = group;
+
+ r = get_group_creds(&g, &i->gid);
+ if (r < 0) {
+ log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
+ goto finish;
+ }
+
+ i->gid_set = true;
+ }
+
+ if (mode && !streq(mode, "-")) {
+ unsigned m;
+
+ if (sscanf(mode, "%o", &m) != 1) {
+ log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
+ r = -ENOENT;
+ goto finish;
+ }
+
+ i->mode = m;
+ i->mode_set = true;
+ } else
+ i->mode =
+ i->type == CREATE_DIRECTORY ||
+ i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
+
+ if (age && !streq(age, "-")) {
+ if (parse_usec(age, &i->age) < 0) {
+ log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ i->age_set = true;
+ }
+
+ h = needs_glob(i->type) ? globs : items;
+
+ existing = hashmap_get(h, i->path);
+ if (existing) {
+
+ /* Two identical items are fine */
+ if (!item_equal(existing, i))
+ log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
+
+ r = 0;
+ goto finish;
+ }
+
+ r = hashmap_put(h, i->path, i);
+ if (r < 0) {
+ log_error("Failed to insert item %s: %s", i->path, strerror(-r));
+ goto finish;
+ }
+
+ i = NULL;
+ r = 0;
+
+finish:
+ free(user);
+ free(group);
+ free(mode);
+ free(age);
+
+ if (i)
+ item_free(i);
+
+ return r;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+ "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
+ " -h --help Show this help\n"
+ " --create Create marked files/directories\n"
+ " --clean Clean up marked directories\n"
+ " --remove Remove marked files/directories\n"
+ " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_CREATE,
+ ARG_CLEAN,
+ ARG_REMOVE,
+ ARG_PREFIX
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "create", no_argument, NULL, ARG_CREATE },
+ { "clean", no_argument, NULL, ARG_CLEAN },
+ { "remove", no_argument, NULL, ARG_REMOVE },
+ { "prefix", required_argument, NULL, ARG_PREFIX },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_CREATE:
+ arg_create = true;
+ break;
+
+ case ARG_CLEAN:
+ arg_clean = true;
+ break;
+
+ case ARG_REMOVE:
+ arg_remove = true;
+ break;
+
+ case ARG_PREFIX:
+ arg_prefix = optarg;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (!arg_clean && !arg_create && !arg_remove) {
+ log_error("You need to specify at least one of --clean, --create or --remove.");
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int read_config_file(const char *fn, bool ignore_enoent) {
+ FILE *f;
+ unsigned v = 0;
+ int r = 0;
+
+ assert(fn);
+
+ f = fopen(fn, "re");
+ if (!f) {
+
+ if (ignore_enoent && errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open %s: %m", fn);
+ return -errno;
+ }
+
+ log_debug("apply: %s\n", fn);
+ for (;;) {
+ char line[LINE_MAX], *l;
+ int k;
+
+ if (!(fgets(line, sizeof(line), f)))
+ break;
+
+ v++;
+
+ l = strstrip(line);
+ if (*l == '#' || *l == 0)
+ continue;
+
+ if ((k = parse_line(fn, v, l)) < 0)
+ if (r == 0)
+ r = k;
+ }
+
+ if (ferror(f)) {
+ log_error("Failed to read from file %s: %m", fn);
+ if (r == 0)
+ r = -EIO;
+ }
+
+ fclose(f);
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ Item *i;
+ Iterator iterator;
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ label_init();
+
+ items = hashmap_new(string_hash_func, string_compare_func);
+ globs = hashmap_new(string_hash_func, string_compare_func);
+
+ if (!items || !globs) {
+ log_error("Out of memory");
+ r = EXIT_FAILURE;
+ goto finish;
+ }
+
+ r = EXIT_SUCCESS;
+
+ if (optind < argc) {
+ int j;
+
+ for (j = optind; j < argc; j++)
+ if (read_config_file(argv[j], false) < 0)
+ r = EXIT_FAILURE;
+
+ } else {
+ char **files, **f;
+
+ r = conf_files_list(&files, ".conf",
+ "/etc/tmpfiles.d",
+ "/run/tmpfiles.d",
+ "/usr/local/lib/tmpfiles.d",
+ "/usr/lib/tmpfiles.d",
+ NULL);
+ if (r < 0) {
+ r = EXIT_FAILURE;
+ log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
+ goto finish;
+ }
+
+ STRV_FOREACH(f, files) {
+ if (read_config_file(*f, true) < 0)
+ r = EXIT_FAILURE;
+ }
+
+ strv_free(files);
+ }
+
+ HASHMAP_FOREACH(i, globs, iterator)
+ process_item(i);
+
+ HASHMAP_FOREACH(i, items, iterator)
+ process_item(i);
+
+finish:
+ while ((i = hashmap_steal_first(items)))
+ item_free(i);
+
+ while ((i = hashmap_steal_first(globs)))
+ item_free(i);
+
+ hashmap_free(items);
+ hashmap_free(globs);
+
+ set_free_free(unix_sockets);
+
+ label_finish();
+
+ return r;
+}
diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c
deleted file mode 100644
index 9fbd7f5..0000000
--- a/src/tty-ask-password-agent.c
+++ /dev/null
@@ -1,754 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stddef.h>
-#include <sys/poll.h>
-#include <sys/inotify.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <sys/signalfd.h>
-#include <fcntl.h>
-
-#include "util.h"
-#include "mkdir.h"
-#include "conf-parser.h"
-#include "utmp-wtmp.h"
-#include "socket-util.h"
-#include "ask-password-api.h"
-#include "strv.h"
-
-static enum {
- ACTION_LIST,
- ACTION_QUERY,
- ACTION_WATCH,
- ACTION_WALL
-} arg_action = ACTION_QUERY;
-
-static bool arg_plymouth = false;
-static bool arg_console = false;
-
-static int ask_password_plymouth(
- const char *message,
- usec_t until,
- const char *flag_file,
- bool accept_cached,
- char ***_passphrases) {
-
- int fd = -1, notify = -1;
- union sockaddr_union sa;
- char *packet = NULL;
- ssize_t k;
- int r, n;
- struct pollfd pollfd[2];
- char buffer[LINE_MAX];
- size_t p = 0;
- enum {
- POLL_SOCKET,
- POLL_INOTIFY
- };
-
- assert(_passphrases);
-
- if (flag_file) {
- if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
- r = -errno;
- goto finish;
- }
- }
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
- r = -errno;
- goto finish;
- }
-
- 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) {
- log_error("Failed to connect to Plymouth: %m");
- r = -errno;
- goto finish;
- }
-
- if (accept_cached) {
- packet = strdup("c");
- n = 1;
- } else
- asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
-
- if (!packet) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
- r = k < 0 ? (int) k : -EIO;
- goto finish;
- }
-
- zero(pollfd);
- pollfd[POLL_SOCKET].fd = fd;
- pollfd[POLL_SOCKET].events = POLLIN;
- pollfd[POLL_INOTIFY].fd = notify;
- pollfd[POLL_INOTIFY].events = POLLIN;
-
- for (;;) {
- int sleep_for = -1, j;
-
- if (until > 0) {
- usec_t y;
-
- y = now(CLOCK_MONOTONIC);
-
- if (y > until) {
- r = -ETIME;
- goto finish;
- }
-
- sleep_for = (int) ((until - y) / USEC_PER_MSEC);
- }
-
- if (flag_file)
- if (access(flag_file, F_OK) < 0) {
- r = -errno;
- goto finish;
- }
-
- if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
-
- if (errno == EINTR)
- continue;
-
- r = -errno;
- goto finish;
- } else if (j == 0) {
- r = -ETIME;
- goto finish;
- }
-
- if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
- flush_fd(notify);
-
- if (pollfd[POLL_SOCKET].revents == 0)
- continue;
-
- if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
- r = k < 0 ? -errno : -EIO;
- goto finish;
- }
-
- p += k;
-
- if (p < 1)
- continue;
-
- if (buffer[0] == 5) {
-
- if (accept_cached) {
- /* Hmm, first try with cached
- * passwords failed, so let's retry
- * with a normal password request */
- free(packet);
- packet = NULL;
-
- if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
- r = k < 0 ? (int) k : -EIO;
- goto finish;
- }
-
- accept_cached = false;
- p = 0;
- continue;
- }
-
- /* No password, because UI not shown */
- r = -ENOENT;
- goto finish;
-
- } else if (buffer[0] == 2 || buffer[0] == 9) {
- uint32_t size;
- char **l;
-
- /* One ore more answers */
- if (p < 5)
- continue;
-
- memcpy(&size, buffer+1, sizeof(size));
- size = le32toh(size);
- if (size+5 > sizeof(buffer)) {
- r = -EIO;
- goto finish;
- }
-
- if (p-5 < size)
- continue;
-
- if (!(l = strv_parse_nulstr(buffer + 5, size))) {
- r = -ENOMEM;
- goto finish;
- }
-
- *_passphrases = l;
- break;
-
- } else {
- /* Unknown packet */
- r = -EIO;
- goto finish;
- }
- }
-
- r = 0;
-
-finish:
- if (notify >= 0)
- close_nointr_nofail(notify);
-
- if (fd >= 0)
- close_nointr_nofail(fd);
-
- free(packet);
-
- return r;
-}
-
-static int parse_password(const char *filename, char **wall) {
- char *socket_name = NULL, *message = NULL, *packet = NULL;
- uint64_t not_after = 0;
- unsigned pid = 0;
- int socket_fd = -1;
- bool accept_cached = false;
-
- const ConfigTableItem items[] = {
- { "Ask", "Socket", config_parse_string, 0, &socket_name },
- { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
- { "Ask", "Message", config_parse_string, 0, &message },
- { "Ask", "PID", config_parse_unsigned, 0, &pid },
- { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
- { NULL, NULL, NULL, 0, NULL }
- };
-
- FILE *f;
- int r;
-
- assert(filename);
-
- f = fopen(filename, "re");
- if (!f) {
- if (errno == ENOENT)
- return 0;
-
- log_error("open(%s): %m", filename);
- return -errno;
- }
-
- r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
- if (r < 0) {
- log_error("Failed to parse password file %s: %s", filename, strerror(-r));
- goto finish;
- }
-
- if (!socket_name) {
- log_error("Invalid password file %s", filename);
- r = -EBADMSG;
- goto finish;
- }
-
- if (not_after > 0) {
- if (now(CLOCK_MONOTONIC) > not_after) {
- r = 0;
- goto finish;
- }
- }
-
- if (pid > 0 &&
- kill(pid, 0) < 0 &&
- errno == ESRCH) {
- r = 0;
- goto finish;
- }
-
- if (arg_action == ACTION_LIST)
- printf("'%s' (PID %u)\n", message, pid);
- else if (arg_action == ACTION_WALL) {
- char *_wall;
-
- if (asprintf(&_wall,
- "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
- "Please enter password with the systemd-tty-ask-password-agent tool!",
- *wall ? *wall : "",
- *wall ? "\r\n\r\n" : "",
- message,
- pid) < 0) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- free(*wall);
- *wall = _wall;
- } else {
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- } sa;
- size_t packet_length = 0;
-
- assert(arg_action == ACTION_QUERY ||
- arg_action == ACTION_WATCH);
-
- if (access(socket_name, W_OK) < 0) {
-
- if (arg_action == ACTION_QUERY)
- log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
-
- r = 0;
- goto finish;
- }
-
- if (arg_plymouth) {
- char **passwords = NULL;
-
- if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
- char **p;
-
- packet_length = 1;
- STRV_FOREACH(p, passwords)
- packet_length += strlen(*p) + 1;
-
- if (!(packet = new(char, packet_length)))
- r = -ENOMEM;
- else {
- char *d;
-
- packet[0] = '+';
- d = packet+1;
-
- STRV_FOREACH(p, passwords)
- d = stpcpy(d, *p) + 1;
- }
- }
-
- } else {
- int tty_fd = -1;
- char *password;
-
- if (arg_console)
- if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
- r = tty_fd;
- goto finish;
- }
-
- r = ask_password_tty(message, not_after, filename, &password);
-
- if (arg_console) {
- close_nointr_nofail(tty_fd);
- release_terminal();
- }
-
- if (r >= 0) {
- packet_length = 1+strlen(password)+1;
- if (!(packet = new(char, packet_length)))
- r = -ENOMEM;
- else {
- packet[0] = '+';
- strcpy(packet+1, password);
- }
-
- free(password);
- }
- }
-
- if (r == -ETIME || r == -ENOENT) {
- /* If the query went away, that's OK */
- r = 0;
- goto finish;
- }
-
- if (r < 0) {
- log_error("Failed to query password: %s", strerror(-r));
- goto finish;
- }
-
- if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
- log_error("socket(): %m");
- r = -errno;
- goto finish;
- }
-
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
- if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
- log_error("Failed to send: %m");
- r = -errno;
- goto finish;
- }
- }
-
-finish:
- fclose(f);
-
- if (socket_fd >= 0)
- close_nointr_nofail(socket_fd);
-
- free(packet);
- free(socket_name);
- free(message);
-
- return r;
-}
-
-static int wall_tty_block(void) {
- char *p;
- int fd, r;
- dev_t devnr;
-
- r = get_ctty_devnr(0, &devnr);
- if (r < 0)
- return -r;
-
- if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
- return -ENOMEM;
-
- mkdir_parents(p, 0700);
- mkfifo(p, 0600);
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- free(p);
-
- if (fd < 0)
- return -errno;
-
- return fd;
-}
-
-static bool wall_tty_match(const char *path) {
- int fd, k;
- char *p;
- struct stat st;
-
- if (path_is_absolute(path))
- k = lstat(path, &st);
- else {
- if (asprintf(&p, "/dev/%s", path) < 0)
- return true;
-
- k = lstat(p, &st);
- free(p);
- }
-
- if (k < 0)
- return true;
-
- if (!S_ISCHR(st.st_mode))
- return true;
-
- /* We use named pipes to ensure that wall messages suggesting
- * password entry are not printed over password prompts
- * already shown. We use the fact here that opening a pipe in
- * non-blocking mode for write-only will succeed only if
- * there's some writer behind it. Using pipes has the
- * advantage that the block will automatically go away if the
- * process dies. */
-
- if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return true;
-
- fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
- free(p);
-
- if (fd < 0)
- return true;
-
- /* What, we managed to open the pipe? Then this tty is filtered. */
- close_nointr_nofail(fd);
- return false;
-}
-
-static int show_passwords(void) {
- DIR *d;
- struct dirent *de;
- int r = 0;
-
- if (!(d = opendir("/run/systemd/ask-password"))) {
- if (errno == ENOENT)
- return 0;
-
- log_error("opendir(): %m");
- return -errno;
- }
-
- while ((de = readdir(d))) {
- char *p;
- int q;
- char *wall;
-
- /* We only support /dev on tmpfs, hence we can rely on
- * d_type to be reliable */
-
- if (de->d_type != DT_REG)
- continue;
-
- if (ignore_file(de->d_name))
- continue;
-
- if (!startswith(de->d_name, "ask."))
- continue;
-
- if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
- log_error("Out of memory");
- r = -ENOMEM;
- goto finish;
- }
-
- wall = NULL;
- if ((q = parse_password(p, &wall)) < 0)
- r = q;
-
- free(p);
-
- if (wall) {
- utmp_wall(wall, wall_tty_match);
- free(wall);
- }
- }
-
-finish:
- if (d)
- closedir(d);
-
- return r;
-}
-
-static int watch_passwords(void) {
- enum {
- FD_INOTIFY,
- FD_SIGNAL,
- _FD_MAX
- };
-
- int notify = -1, signal_fd = -1, tty_block_fd = -1;
- struct pollfd pollfd[_FD_MAX];
- sigset_t mask;
- int r;
-
- tty_block_fd = wall_tty_block();
-
- mkdir_p("/run/systemd/ask-password", 0755);
-
- if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
- r = -errno;
- goto finish;
- }
-
- if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
- r = -errno;
- goto finish;
- }
-
- assert_se(sigemptyset(&mask) == 0);
- sigset_add_many(&mask, SIGINT, SIGTERM, -1);
- assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
- if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
- log_error("signalfd(): %m");
- r = -errno;
- goto finish;
- }
-
- zero(pollfd);
- pollfd[FD_INOTIFY].fd = notify;
- pollfd[FD_INOTIFY].events = POLLIN;
- pollfd[FD_SIGNAL].fd = signal_fd;
- pollfd[FD_SIGNAL].events = POLLIN;
-
- for (;;) {
- if ((r = show_passwords()) < 0)
- log_error("Failed to show password: %s", strerror(-r));
-
- if (poll(pollfd, _FD_MAX, -1) < 0) {
-
- if (errno == EINTR)
- continue;
-
- r = -errno;
- goto finish;
- }
-
- if (pollfd[FD_INOTIFY].revents != 0)
- flush_fd(notify);
-
- if (pollfd[FD_SIGNAL].revents != 0)
- break;
- }
-
- r = 0;
-
-finish:
- if (notify >= 0)
- close_nointr_nofail(notify);
-
- if (signal_fd >= 0)
- close_nointr_nofail(signal_fd);
-
- if (tty_block_fd >= 0)
- close_nointr_nofail(tty_block_fd);
-
- return r;
-}
-
-static int help(void) {
-
- printf("%s [OPTIONS...]\n\n"
- "Process system password requests.\n\n"
- " -h --help Show this help\n"
- " --list Show pending password requests\n"
- " --query Process pending password requests\n"
- " --watch Continuously process password requests\n"
- " --wall Continuously forward password requests to wall\n"
- " --plymouth Ask question with Plymouth instead of on TTY\n"
- " --console Ask question on /dev/console instead of current TTY\n",
- program_invocation_short_name);
-
- return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
- enum {
- ARG_LIST = 0x100,
- ARG_QUERY,
- ARG_WATCH,
- ARG_WALL,
- ARG_PLYMOUTH,
- ARG_CONSOLE
- };
-
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "list", no_argument, NULL, ARG_LIST },
- { "query", no_argument, NULL, ARG_QUERY },
- { "watch", no_argument, NULL, ARG_WATCH },
- { "wall", no_argument, NULL, ARG_WALL },
- { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
- { "console", no_argument, NULL, ARG_CONSOLE },
- { NULL, 0, NULL, 0 }
- };
-
- int c;
-
- assert(argc >= 0);
- assert(argv);
-
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
- switch (c) {
-
- case 'h':
- help();
- return 0;
-
- case ARG_LIST:
- arg_action = ACTION_LIST;
- break;
-
- case ARG_QUERY:
- arg_action = ACTION_QUERY;
- break;
-
- case ARG_WATCH:
- arg_action = ACTION_WATCH;
- break;
-
- case ARG_WALL:
- arg_action = ACTION_WALL;
- break;
-
- case ARG_PLYMOUTH:
- arg_plymouth = true;
- break;
-
- case ARG_CONSOLE:
- arg_console = true;
- break;
-
- case '?':
- return -EINVAL;
-
- default:
- log_error("Unknown option code %c", c);
- return -EINVAL;
- }
- }
-
- if (optind != argc) {
- help();
- return -EINVAL;
- }
-
- return 1;
-}
-
-int main(int argc, char *argv[]) {
- int r;
-
- log_parse_environment();
- log_open();
-
- umask(0022);
-
- if ((r = parse_argv(argc, argv)) <= 0)
- goto finish;
-
- if (arg_console) {
- setsid();
- release_terminal();
- }
-
- if (arg_action == ACTION_WATCH ||
- arg_action == ACTION_WALL)
- r = watch_passwords();
- else
- r = show_passwords();
-
- if (r < 0)
- log_error("Error: %s", strerror(-r));
-
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
new file mode 100644
index 0000000..9fbd7f5
--- /dev/null
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -0,0 +1,754 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stddef.h>
+#include <sys/poll.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/signalfd.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "mkdir.h"
+#include "conf-parser.h"
+#include "utmp-wtmp.h"
+#include "socket-util.h"
+#include "ask-password-api.h"
+#include "strv.h"
+
+static enum {
+ ACTION_LIST,
+ ACTION_QUERY,
+ ACTION_WATCH,
+ ACTION_WALL
+} arg_action = ACTION_QUERY;
+
+static bool arg_plymouth = false;
+static bool arg_console = false;
+
+static int ask_password_plymouth(
+ const char *message,
+ usec_t until,
+ const char *flag_file,
+ bool accept_cached,
+ char ***_passphrases) {
+
+ int fd = -1, notify = -1;
+ union sockaddr_union sa;
+ char *packet = NULL;
+ ssize_t k;
+ int r, n;
+ struct pollfd pollfd[2];
+ char buffer[LINE_MAX];
+ size_t p = 0;
+ enum {
+ POLL_SOCKET,
+ POLL_INOTIFY
+ };
+
+ assert(_passphrases);
+
+ if (flag_file) {
+ if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ 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) {
+ log_error("Failed to connect to Plymouth: %m");
+ r = -errno;
+ goto finish;
+ }
+
+ if (accept_cached) {
+ packet = strdup("c");
+ n = 1;
+ } else
+ asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
+
+ if (!packet) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
+ r = k < 0 ? (int) k : -EIO;
+ goto finish;
+ }
+
+ zero(pollfd);
+ pollfd[POLL_SOCKET].fd = fd;
+ pollfd[POLL_SOCKET].events = POLLIN;
+ pollfd[POLL_INOTIFY].fd = notify;
+ pollfd[POLL_INOTIFY].events = POLLIN;
+
+ for (;;) {
+ int sleep_for = -1, j;
+
+ if (until > 0) {
+ usec_t y;
+
+ y = now(CLOCK_MONOTONIC);
+
+ if (y > until) {
+ r = -ETIME;
+ goto finish;
+ }
+
+ sleep_for = (int) ((until - y) / USEC_PER_MSEC);
+ }
+
+ if (flag_file)
+ if (access(flag_file, F_OK) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ r = -errno;
+ goto finish;
+ } else if (j == 0) {
+ r = -ETIME;
+ goto finish;
+ }
+
+ if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
+ flush_fd(notify);
+
+ if (pollfd[POLL_SOCKET].revents == 0)
+ continue;
+
+ if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
+ r = k < 0 ? -errno : -EIO;
+ goto finish;
+ }
+
+ p += k;
+
+ if (p < 1)
+ continue;
+
+ if (buffer[0] == 5) {
+
+ if (accept_cached) {
+ /* Hmm, first try with cached
+ * passwords failed, so let's retry
+ * with a normal password request */
+ free(packet);
+ packet = NULL;
+
+ if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
+ r = k < 0 ? (int) k : -EIO;
+ goto finish;
+ }
+
+ accept_cached = false;
+ p = 0;
+ continue;
+ }
+
+ /* No password, because UI not shown */
+ r = -ENOENT;
+ goto finish;
+
+ } else if (buffer[0] == 2 || buffer[0] == 9) {
+ uint32_t size;
+ char **l;
+
+ /* One ore more answers */
+ if (p < 5)
+ continue;
+
+ memcpy(&size, buffer+1, sizeof(size));
+ size = le32toh(size);
+ if (size+5 > sizeof(buffer)) {
+ r = -EIO;
+ goto finish;
+ }
+
+ if (p-5 < size)
+ continue;
+
+ if (!(l = strv_parse_nulstr(buffer + 5, size))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ *_passphrases = l;
+ break;
+
+ } else {
+ /* Unknown packet */
+ r = -EIO;
+ goto finish;
+ }
+ }
+
+ r = 0;
+
+finish:
+ if (notify >= 0)
+ close_nointr_nofail(notify);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ free(packet);
+
+ return r;
+}
+
+static int parse_password(const char *filename, char **wall) {
+ char *socket_name = NULL, *message = NULL, *packet = NULL;
+ uint64_t not_after = 0;
+ unsigned pid = 0;
+ int socket_fd = -1;
+ bool accept_cached = false;
+
+ const ConfigTableItem items[] = {
+ { "Ask", "Socket", config_parse_string, 0, &socket_name },
+ { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after },
+ { "Ask", "Message", config_parse_string, 0, &message },
+ { "Ask", "PID", config_parse_unsigned, 0, &pid },
+ { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached },
+ { NULL, NULL, NULL, 0, NULL }
+ };
+
+ FILE *f;
+ int r;
+
+ assert(filename);
+
+ f = fopen(filename, "re");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("open(%s): %m", filename);
+ return -errno;
+ }
+
+ r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
+ if (r < 0) {
+ log_error("Failed to parse password file %s: %s", filename, strerror(-r));
+ goto finish;
+ }
+
+ if (!socket_name) {
+ log_error("Invalid password file %s", filename);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (not_after > 0) {
+ if (now(CLOCK_MONOTONIC) > not_after) {
+ r = 0;
+ goto finish;
+ }
+ }
+
+ if (pid > 0 &&
+ kill(pid, 0) < 0 &&
+ errno == ESRCH) {
+ r = 0;
+ goto finish;
+ }
+
+ if (arg_action == ACTION_LIST)
+ printf("'%s' (PID %u)\n", message, pid);
+ else if (arg_action == ACTION_WALL) {
+ char *_wall;
+
+ if (asprintf(&_wall,
+ "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
+ "Please enter password with the systemd-tty-ask-password-agent tool!",
+ *wall ? *wall : "",
+ *wall ? "\r\n\r\n" : "",
+ message,
+ pid) < 0) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ free(*wall);
+ *wall = _wall;
+ } else {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+ size_t packet_length = 0;
+
+ assert(arg_action == ACTION_QUERY ||
+ arg_action == ACTION_WATCH);
+
+ if (access(socket_name, W_OK) < 0) {
+
+ if (arg_action == ACTION_QUERY)
+ log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
+
+ r = 0;
+ goto finish;
+ }
+
+ if (arg_plymouth) {
+ char **passwords = NULL;
+
+ if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
+ char **p;
+
+ packet_length = 1;
+ STRV_FOREACH(p, passwords)
+ packet_length += strlen(*p) + 1;
+
+ if (!(packet = new(char, packet_length)))
+ r = -ENOMEM;
+ else {
+ char *d;
+
+ packet[0] = '+';
+ d = packet+1;
+
+ STRV_FOREACH(p, passwords)
+ d = stpcpy(d, *p) + 1;
+ }
+ }
+
+ } else {
+ int tty_fd = -1;
+ char *password;
+
+ if (arg_console)
+ if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
+ r = tty_fd;
+ goto finish;
+ }
+
+ r = ask_password_tty(message, not_after, filename, &password);
+
+ if (arg_console) {
+ close_nointr_nofail(tty_fd);
+ release_terminal();
+ }
+
+ if (r >= 0) {
+ packet_length = 1+strlen(password)+1;
+ if (!(packet = new(char, packet_length)))
+ r = -ENOMEM;
+ else {
+ packet[0] = '+';
+ strcpy(packet+1, password);
+ }
+
+ free(password);
+ }
+ }
+
+ if (r == -ETIME || r == -ENOENT) {
+ /* If the query went away, that's OK */
+ r = 0;
+ goto finish;
+ }
+
+ if (r < 0) {
+ log_error("Failed to query password: %s", strerror(-r));
+ goto finish;
+ }
+
+ if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
+ log_error("socket(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+
+ if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
+ log_error("Failed to send: %m");
+ r = -errno;
+ goto finish;
+ }
+ }
+
+finish:
+ fclose(f);
+
+ if (socket_fd >= 0)
+ close_nointr_nofail(socket_fd);
+
+ free(packet);
+ free(socket_name);
+ free(message);
+
+ return r;
+}
+
+static int wall_tty_block(void) {
+ char *p;
+ int fd, r;
+ dev_t devnr;
+
+ r = get_ctty_devnr(0, &devnr);
+ if (r < 0)
+ return -r;
+
+ if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
+ return -ENOMEM;
+
+ mkdir_parents(p, 0700);
+ mkfifo(p, 0600);
+
+ fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ free(p);
+
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+static bool wall_tty_match(const char *path) {
+ int fd, k;
+ char *p;
+ struct stat st;
+
+ if (path_is_absolute(path))
+ k = lstat(path, &st);
+ else {
+ if (asprintf(&p, "/dev/%s", path) < 0)
+ return true;
+
+ k = lstat(p, &st);
+ free(p);
+ }
+
+ if (k < 0)
+ return true;
+
+ if (!S_ISCHR(st.st_mode))
+ return true;
+
+ /* We use named pipes to ensure that wall messages suggesting
+ * password entry are not printed over password prompts
+ * already shown. We use the fact here that opening a pipe in
+ * non-blocking mode for write-only will succeed only if
+ * there's some writer behind it. Using pipes has the
+ * advantage that the block will automatically go away if the
+ * process dies. */
+
+ if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return true;
+
+ fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+ free(p);
+
+ if (fd < 0)
+ return true;
+
+ /* What, we managed to open the pipe? Then this tty is filtered. */
+ close_nointr_nofail(fd);
+ return false;
+}
+
+static int show_passwords(void) {
+ DIR *d;
+ struct dirent *de;
+ int r = 0;
+
+ if (!(d = opendir("/run/systemd/ask-password"))) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("opendir(): %m");
+ return -errno;
+ }
+
+ while ((de = readdir(d))) {
+ char *p;
+ int q;
+ char *wall;
+
+ /* We only support /dev on tmpfs, hence we can rely on
+ * d_type to be reliable */
+
+ if (de->d_type != DT_REG)
+ continue;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ if (!startswith(de->d_name, "ask."))
+ continue;
+
+ if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ wall = NULL;
+ if ((q = parse_password(p, &wall)) < 0)
+ r = q;
+
+ free(p);
+
+ if (wall) {
+ utmp_wall(wall, wall_tty_match);
+ free(wall);
+ }
+ }
+
+finish:
+ if (d)
+ closedir(d);
+
+ return r;
+}
+
+static int watch_passwords(void) {
+ enum {
+ FD_INOTIFY,
+ FD_SIGNAL,
+ _FD_MAX
+ };
+
+ int notify = -1, signal_fd = -1, tty_block_fd = -1;
+ struct pollfd pollfd[_FD_MAX];
+ sigset_t mask;
+ int r;
+
+ tty_block_fd = wall_tty_block();
+
+ mkdir_p("/run/systemd/ask-password", 0755);
+
+ if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ assert_se(sigemptyset(&mask) == 0);
+ sigset_add_many(&mask, SIGINT, SIGTERM, -1);
+ assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+ if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+ log_error("signalfd(): %m");
+ r = -errno;
+ goto finish;
+ }
+
+ zero(pollfd);
+ pollfd[FD_INOTIFY].fd = notify;
+ pollfd[FD_INOTIFY].events = POLLIN;
+ pollfd[FD_SIGNAL].fd = signal_fd;
+ pollfd[FD_SIGNAL].events = POLLIN;
+
+ for (;;) {
+ if ((r = show_passwords()) < 0)
+ log_error("Failed to show password: %s", strerror(-r));
+
+ if (poll(pollfd, _FD_MAX, -1) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ r = -errno;
+ goto finish;
+ }
+
+ if (pollfd[FD_INOTIFY].revents != 0)
+ flush_fd(notify);
+
+ if (pollfd[FD_SIGNAL].revents != 0)
+ break;
+ }
+
+ r = 0;
+
+finish:
+ if (notify >= 0)
+ close_nointr_nofail(notify);
+
+ if (signal_fd >= 0)
+ close_nointr_nofail(signal_fd);
+
+ if (tty_block_fd >= 0)
+ close_nointr_nofail(tty_block_fd);
+
+ return r;
+}
+
+static int help(void) {
+
+ printf("%s [OPTIONS...]\n\n"
+ "Process system password requests.\n\n"
+ " -h --help Show this help\n"
+ " --list Show pending password requests\n"
+ " --query Process pending password requests\n"
+ " --watch Continuously process password requests\n"
+ " --wall Continuously forward password requests to wall\n"
+ " --plymouth Ask question with Plymouth instead of on TTY\n"
+ " --console Ask question on /dev/console instead of current TTY\n",
+ program_invocation_short_name);
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_LIST = 0x100,
+ ARG_QUERY,
+ ARG_WATCH,
+ ARG_WALL,
+ ARG_PLYMOUTH,
+ ARG_CONSOLE
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "list", no_argument, NULL, ARG_LIST },
+ { "query", no_argument, NULL, ARG_QUERY },
+ { "watch", no_argument, NULL, ARG_WATCH },
+ { "wall", no_argument, NULL, ARG_WALL },
+ { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
+ { "console", no_argument, NULL, ARG_CONSOLE },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ help();
+ return 0;
+
+ case ARG_LIST:
+ arg_action = ACTION_LIST;
+ break;
+
+ case ARG_QUERY:
+ arg_action = ACTION_QUERY;
+ break;
+
+ case ARG_WATCH:
+ arg_action = ACTION_WATCH;
+ break;
+
+ case ARG_WALL:
+ arg_action = ACTION_WALL;
+ break;
+
+ case ARG_PLYMOUTH:
+ arg_plymouth = true;
+ break;
+
+ case ARG_CONSOLE:
+ arg_console = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+ }
+
+ if (optind != argc) {
+ help();
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+ if ((r = parse_argv(argc, argv)) <= 0)
+ goto finish;
+
+ if (arg_console) {
+ setsid();
+ release_terminal();
+ }
+
+ if (arg_action == ACTION_WATCH ||
+ arg_action == ACTION_WALL)
+ r = watch_passwords();
+ else
+ r = show_passwords();
+
+ if (r < 0)
+ log_error("Error: %s", strerror(-r));
+
+finish:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/update-utmp.c b/src/update-utmp.c
deleted file mode 100644
index ec07b92..0000000
--- a/src/update-utmp.c
+++ /dev/null
@@ -1,423 +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 Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <dbus/dbus.h>
-
-#ifdef HAVE_AUDIT
-#include <libaudit.h>
-#endif
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "special.h"
-#include "utmp-wtmp.h"
-#include "dbus-common.h"
-
-typedef struct Context {
- DBusConnection *bus;
-#ifdef HAVE_AUDIT
- int audit_fd;
-#endif
-} Context;
-
-static usec_t get_startup_time(Context *c) {
- const char
- *interface = "org.freedesktop.systemd1.Manager",
- *property = "StartupTimestamp";
-
- DBusError error;
- usec_t t = 0;
- DBusMessage *m = NULL, *reply = NULL;
- DBusMessageIter iter, sub;
-
- dbus_error_init(&error);
-
- assert(c);
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.DBus.Properties",
- "Get"))) {
- log_error("Could not allocate message.");
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
- log_error("Failed to send command: %s", bus_error_message(&error));
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
- log_error("Failed to parse reply.");
- goto finish;
- }
-
- dbus_message_iter_get_basic(&sub, &t);
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return t;
-}
-
-static int get_current_runlevel(Context *c) {
- static const struct {
- const int runlevel;
- const char *special;
- } table[] = {
- /* The first target of this list that is active or has
- * a job scheduled wins. We prefer runlevels 5 and 3
- * here over the others, since these are the main
- * runlevels used on Fedora. It might make sense to
- * change the order on some distributions. */
- { '5', SPECIAL_RUNLEVEL5_TARGET },
- { '3', SPECIAL_RUNLEVEL3_TARGET },
- { '4', SPECIAL_RUNLEVEL4_TARGET },
- { '2', SPECIAL_RUNLEVEL2_TARGET },
- { 'S', SPECIAL_RESCUE_TARGET },
- };
- const char
- *interface = "org.freedesktop.systemd1.Unit",
- *property = "ActiveState";
-
- DBusMessage *m = NULL, *reply = NULL;
- int r = 0;
- unsigned i;
- DBusError error;
-
- assert(c);
-
- dbus_error_init(&error);
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
- const char *path = NULL, *state;
- DBusMessageIter iter, sub;
-
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnit"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &table[i].special,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
- dbus_error_free(&error);
- continue;
- }
-
- if (!dbus_message_get_args(reply, &error,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID)) {
- log_error("Failed to parse reply: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- dbus_message_unref(m);
- if (!(m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.DBus.Properties",
- "Get"))) {
- log_error("Could not allocate message.");
- r = -ENOMEM;
- goto finish;
- }
-
- if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID)) {
- log_error("Could not append arguments to message.");
- r = -ENOMEM;
- goto finish;
- }
-
- dbus_message_unref(reply);
- if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
- log_error("Failed to send command: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- if (!dbus_message_iter_init(reply, &iter) ||
- dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
- log_error("Failed to parse reply.");
- r = -EIO;
- goto finish;
- }
-
- dbus_message_iter_get_basic(&sub, &state);
-
- if (streq(state, "active") || streq(state, "reloading"))
- r = table[i].runlevel;
-
- dbus_message_unref(m);
- dbus_message_unref(reply);
- m = reply = NULL;
-
- if (r)
- break;
- }
-
-finish:
- if (m)
- dbus_message_unref(m);
-
- if (reply)
- dbus_message_unref(reply);
-
- dbus_error_free(&error);
-
- return r;
-}
-
-static int on_reboot(Context *c) {
- int r = 0, q;
- usec_t t;
-
- assert(c);
-
- /* We finished start-up, so let's write the utmp
- * record and send the audit msg */
-
-#ifdef HAVE_AUDIT
- if (c->audit_fd >= 0)
- if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0) {
- log_error("Failed to send audit message: %m");
- r = -errno;
- }
-#endif
-
- /* If this call fails it will return 0, which
- * utmp_put_reboot() will then fix to the current time */
- t = get_startup_time(c);
-
- if ((q = utmp_put_reboot(t)) < 0) {
- log_error("Failed to write utmp record: %s", strerror(-q));
- r = q;
- }
-
- return r;
-}
-
-static int on_shutdown(Context *c) {
- int r = 0, q;
-
- assert(c);
-
- /* We started shut-down, so let's write the utmp
- * record and send the audit msg */
-
-#ifdef HAVE_AUDIT
- if (c->audit_fd >= 0)
- if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0) {
- log_error("Failed to send audit message: %m");
- r = -errno;
- }
-#endif
-
- if ((q = utmp_put_shutdown()) < 0) {
- log_error("Failed to write utmp record: %s", strerror(-q));
- r = q;
- }
-
- return r;
-}
-
-static int on_runlevel(Context *c) {
- int r = 0, q, previous, runlevel;
-
- assert(c);
-
- /* We finished changing runlevel, so let's write the
- * utmp record and send the audit msg */
-
- /* First, get last runlevel */
- if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
-
- if (q != -ESRCH && q != -ENOENT) {
- log_error("Failed to get current runlevel: %s", strerror(-q));
- return q;
- }
-
- /* Hmm, we didn't find any runlevel, that means we
- * have been rebooted */
- r = on_reboot(c);
- previous = 0;
- }
-
- /* Secondly, get new runlevel */
- if ((runlevel = get_current_runlevel(c)) < 0)
- return runlevel;
-
- if (previous == runlevel)
- return 0;
-
-#ifdef HAVE_AUDIT
- if (c->audit_fd >= 0) {
- char *s = NULL;
-
- if (asprintf(&s, "old-level=%c new-level=%c",
- previous > 0 ? previous : 'N',
- runlevel > 0 ? runlevel : 'N') < 0)
- return -ENOMEM;
-
- if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0) {
- log_error("Failed to send audit message: %m");
- r = -errno;
- }
-
- free(s);
- }
-#endif
-
- if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
- log_error("Failed to write utmp record: %s", strerror(-q));
- r = q;
- }
-
- return r;
-}
-
-int main(int argc, char *argv[]) {
- int r;
- DBusError error;
- Context c;
-
- dbus_error_init(&error);
-
- zero(c);
-#ifdef HAVE_AUDIT
- c.audit_fd = -1;
-#endif
-
- if (getppid() != 1) {
- log_error("This program should be invoked by init only.");
- return EXIT_FAILURE;
- }
-
- if (argc != 2) {
- log_error("This program requires one argument.");
- return EXIT_FAILURE;
- }
-
- log_set_target(LOG_TARGET_AUTO);
- log_parse_environment();
- log_open();
-
- umask(0022);
-
-#ifdef HAVE_AUDIT
- if ((c.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
-
- if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
- log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
- r = -EIO;
- goto finish;
- }
-
- log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
-
- if (streq(argv[1], "reboot"))
- r = on_reboot(&c);
- else if (streq(argv[1], "shutdown"))
- r = on_shutdown(&c);
- else if (streq(argv[1], "runlevel"))
- r = on_runlevel(&c);
- else {
- log_error("Unknown command %s", argv[1]);
- r = -EINVAL;
- }
-
- log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
-
-finish:
-#ifdef HAVE_AUDIT
- if (c.audit_fd >= 0)
- audit_close(c.audit_fd);
-#endif
-
- if (c.bus) {
- dbus_connection_flush(c.bus);
- dbus_connection_close(c.bus);
- dbus_connection_unref(c.bus);
- }
-
- dbus_error_free(&error);
- dbus_shutdown();
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
new file mode 100644
index 0000000..ec07b92
--- /dev/null
+++ b/src/update-utmp/update-utmp.c
@@ -0,0 +1,423 @@
+/*-*- 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 Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
+
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "special.h"
+#include "utmp-wtmp.h"
+#include "dbus-common.h"
+
+typedef struct Context {
+ DBusConnection *bus;
+#ifdef HAVE_AUDIT
+ int audit_fd;
+#endif
+} Context;
+
+static usec_t get_startup_time(Context *c) {
+ const char
+ *interface = "org.freedesktop.systemd1.Manager",
+ *property = "StartupTimestamp";
+
+ DBusError error;
+ usec_t t = 0;
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusMessageIter iter, sub;
+
+ dbus_error_init(&error);
+
+ assert(c);
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.DBus.Properties",
+ "Get"))) {
+ log_error("Could not allocate message.");
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+ log_error("Failed to send command: %s", bus_error_message(&error));
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
+ log_error("Failed to parse reply.");
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, &t);
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return t;
+}
+
+static int get_current_runlevel(Context *c) {
+ static const struct {
+ const int runlevel;
+ const char *special;
+ } table[] = {
+ /* The first target of this list that is active or has
+ * a job scheduled wins. We prefer runlevels 5 and 3
+ * here over the others, since these are the main
+ * runlevels used on Fedora. It might make sense to
+ * change the order on some distributions. */
+ { '5', SPECIAL_RUNLEVEL5_TARGET },
+ { '3', SPECIAL_RUNLEVEL3_TARGET },
+ { '4', SPECIAL_RUNLEVEL4_TARGET },
+ { '2', SPECIAL_RUNLEVEL2_TARGET },
+ { 'S', SPECIAL_RESCUE_TARGET },
+ };
+ const char
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "ActiveState";
+
+ DBusMessage *m = NULL, *reply = NULL;
+ int r = 0;
+ unsigned i;
+ DBusError error;
+
+ assert(c);
+
+ dbus_error_init(&error);
+
+ for (i = 0; i < ELEMENTSOF(table); i++) {
+ const char *path = NULL, *state;
+ DBusMessageIter iter, sub;
+
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &table[i].special,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+ dbus_error_free(&error);
+ continue;
+ }
+
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_unref(m);
+ if (!(m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get"))) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ dbus_message_unref(reply);
+ if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+ log_error("Failed to send command: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, &state);
+
+ if (streq(state, "active") || streq(state, "reloading"))
+ r = table[i].runlevel;
+
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
+
+ if (r)
+ break;
+ }
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
+static int on_reboot(Context *c) {
+ int r = 0, q;
+ usec_t t;
+
+ assert(c);
+
+ /* We finished start-up, so let's write the utmp
+ * record and send the audit msg */
+
+#ifdef HAVE_AUDIT
+ if (c->audit_fd >= 0)
+ if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0) {
+ log_error("Failed to send audit message: %m");
+ r = -errno;
+ }
+#endif
+
+ /* If this call fails it will return 0, which
+ * utmp_put_reboot() will then fix to the current time */
+ t = get_startup_time(c);
+
+ if ((q = utmp_put_reboot(t)) < 0) {
+ log_error("Failed to write utmp record: %s", strerror(-q));
+ r = q;
+ }
+
+ return r;
+}
+
+static int on_shutdown(Context *c) {
+ int r = 0, q;
+
+ assert(c);
+
+ /* We started shut-down, so let's write the utmp
+ * record and send the audit msg */
+
+#ifdef HAVE_AUDIT
+ if (c->audit_fd >= 0)
+ if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0) {
+ log_error("Failed to send audit message: %m");
+ r = -errno;
+ }
+#endif
+
+ if ((q = utmp_put_shutdown()) < 0) {
+ log_error("Failed to write utmp record: %s", strerror(-q));
+ r = q;
+ }
+
+ return r;
+}
+
+static int on_runlevel(Context *c) {
+ int r = 0, q, previous, runlevel;
+
+ assert(c);
+
+ /* We finished changing runlevel, so let's write the
+ * utmp record and send the audit msg */
+
+ /* First, get last runlevel */
+ if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
+
+ if (q != -ESRCH && q != -ENOENT) {
+ log_error("Failed to get current runlevel: %s", strerror(-q));
+ return q;
+ }
+
+ /* Hmm, we didn't find any runlevel, that means we
+ * have been rebooted */
+ r = on_reboot(c);
+ previous = 0;
+ }
+
+ /* Secondly, get new runlevel */
+ if ((runlevel = get_current_runlevel(c)) < 0)
+ return runlevel;
+
+ if (previous == runlevel)
+ return 0;
+
+#ifdef HAVE_AUDIT
+ if (c->audit_fd >= 0) {
+ char *s = NULL;
+
+ if (asprintf(&s, "old-level=%c new-level=%c",
+ previous > 0 ? previous : 'N',
+ runlevel > 0 ? runlevel : 'N') < 0)
+ return -ENOMEM;
+
+ if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0) {
+ log_error("Failed to send audit message: %m");
+ r = -errno;
+ }
+
+ free(s);
+ }
+#endif
+
+ if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
+ log_error("Failed to write utmp record: %s", strerror(-q));
+ r = q;
+ }
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ int r;
+ DBusError error;
+ Context c;
+
+ dbus_error_init(&error);
+
+ zero(c);
+#ifdef HAVE_AUDIT
+ c.audit_fd = -1;
+#endif
+
+ if (getppid() != 1) {
+ log_error("This program should be invoked by init only.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc != 2) {
+ log_error("This program requires one argument.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_AUTO);
+ log_parse_environment();
+ log_open();
+
+ umask(0022);
+
+#ifdef HAVE_AUDIT
+ if ((c.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
+
+ if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
+ log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
+
+ if (streq(argv[1], "reboot"))
+ r = on_reboot(&c);
+ else if (streq(argv[1], "shutdown"))
+ r = on_shutdown(&c);
+ else if (streq(argv[1], "runlevel"))
+ r = on_runlevel(&c);
+ else {
+ log_error("Unknown command %s", argv[1]);
+ r = -EINVAL;
+ }
+
+ log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
+
+finish:
+#ifdef HAVE_AUDIT
+ if (c.audit_fd >= 0)
+ audit_close(c.audit_fd);
+#endif
+
+ if (c.bus) {
+ dbus_connection_flush(c.bus);
+ dbus_connection_close(c.bus);
+ dbus_connection_unref(c.bus);
+ }
+
+ dbus_error_free(&error);
+ dbus_shutdown();
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
More information about the systemd-commits
mailing list