diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/71-seat.rules | 2 | ||||
-rw-r--r-- | src/99-systemd.rules | 7 | ||||
-rw-r--r-- | src/cgls.c | 23 | ||||
-rw-r--r-- | src/cgroup.c | 3 | ||||
-rw-r--r-- | src/condition.c | 6 | ||||
-rw-r--r-- | src/condition.h | 1 | ||||
-rw-r--r-- | src/dbus-common.c | 150 | ||||
-rw-r--r-- | src/dbus-common.h | 4 | ||||
-rw-r--r-- | src/fdset.c | 6 | ||||
-rw-r--r-- | src/load-fragment.c | 12 | ||||
-rw-r--r-- | src/loginctl.c | 1513 | ||||
-rw-r--r-- | src/logind-acl.c | 14 | ||||
-rw-r--r-- | src/logind-acl.h | 20 | ||||
-rw-r--r-- | src/logind-dbus.c | 3 | ||||
-rw-r--r-- | src/logind-session-dbus.c | 2 | ||||
-rw-r--r-- | src/logind.c | 4 | ||||
-rw-r--r-- | src/main.c | 5 | ||||
-rw-r--r-- | src/manager.c | 43 | ||||
-rw-r--r-- | src/manager.h | 4 | ||||
-rw-r--r-- | src/org.freedesktop.hostname1.policy.in (renamed from src/org.freedesktop.hostname1.policy) | 12 | ||||
-rw-r--r-- | src/org.freedesktop.locale1.policy.in (renamed from src/org.freedesktop.locale1.policy) | 4 | ||||
-rw-r--r-- | src/org.freedesktop.login1.policy.in (renamed from src/org.freedesktop.login1.policy) | 12 | ||||
-rw-r--r-- | src/org.freedesktop.timedate1.policy.in (renamed from src/org.freedesktop.timedate1.policy) | 14 | ||||
-rw-r--r-- | src/pager.c | 122 | ||||
-rw-r--r-- | src/pager.h | 28 | ||||
-rw-r--r-- | src/path.c | 8 | ||||
-rw-r--r-- | src/path.h | 1 | ||||
-rw-r--r-- | src/service.c | 4 | ||||
-rw-r--r-- | src/snapshot.c | 2 | ||||
-rw-r--r-- | src/sysfs-show.c | 191 | ||||
-rw-r--r-- | src/sysfs-show.h | 27 | ||||
-rw-r--r-- | src/systemctl.c | 269 | ||||
-rw-r--r-- | src/tty-ask-password-agent.c | 18 | ||||
-rw-r--r-- | src/unit.c | 10 | ||||
-rw-r--r-- | src/util.c | 45 | ||||
-rw-r--r-- | src/util.h | 2 |
36 files changed, 2256 insertions, 335 deletions
diff --git a/src/71-seat.rules b/src/71-seat.rules index af174f9260..6e19d5b34d 100644 --- a/src/71-seat.rules +++ b/src/71-seat.rules @@ -9,7 +9,7 @@ ACTION=="remove", GOTO="seat_end" TAG=="uaccess", SUBSYSTEM!="sound", TAG+="seat" SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat" -SUBSYSTEM=="input", TAG+="seat" +SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat" SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat" SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" diff --git a/src/99-systemd.rules b/src/99-systemd.rules index c079c35304..f015d5e67b 100644 --- a/src/99-systemd.rules +++ b/src/99-systemd.rules @@ -10,6 +10,8 @@ ACTION=="remove", GOTO="systemd_end" SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*", TAG+="systemd" +KERNEL=="vport*", TAG+="systemd" + SUBSYSTEM=="block", KERNEL!="ram*|loop*", TAG+="systemd" SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" @@ -32,8 +34,11 @@ SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsyst SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/bluetooth/devices/%k" SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}="bluetooth.target" -SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" ENV{ID_SMARTCARD_READER}=="*?", TAG+="systemd", ENV{SYSTEMD_WANTS}="smartcard.target" SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}="sound.target" +SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" +SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" + LABEL="systemd_end" diff --git a/src/cgls.c b/src/cgls.c index 2bde743acf..20d6531123 100644 --- a/src/cgls.c +++ b/src/cgls.c @@ -30,20 +30,29 @@ #include "cgroup-util.h" #include "log.h" #include "util.h" +#include "pager.h" + +static bool arg_no_pager = false; static void help(void) { printf("%s [OPTIONS...] [CGROUP...]\n\n" "Recursively show control group contents.\n\n" - " -h --help Show this help\n", + " -h --help Show this help\n" + " --no-pager Do not pipe output into a pager\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' }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { NULL, 0, NULL, 0 } }; int c; @@ -59,6 +68,10 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; + case ARG_NO_PAGER: + arg_no_pager = true; + break; + case '?': return -EINVAL; @@ -84,6 +97,9 @@ int main(int argc, char *argv[]) { goto finish; } + if (!arg_no_pager) + pager_open(); + if (optind < argc) { unsigned i; @@ -132,6 +148,7 @@ int main(int argc, char *argv[]) { retval = EXIT_SUCCESS; finish: + pager_close(); return retval; } diff --git a/src/cgroup.c b/src/cgroup.c index d16b5f878f..4aa01f1898 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -46,9 +46,6 @@ int cgroup_bonding_realize(CGroupBonding *b) { b->realized = true; - if (b->ours) - cg_trim(b->controller, b->path, false); - return 0; } diff --git a/src/condition.c b/src/condition.c index a520e43436..76ee0370d2 100644 --- a/src/condition.c +++ b/src/condition.c @@ -34,6 +34,8 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; + assert(type < _CONDITION_TYPE_MAX); + if (!(c = new0(Condition, 1))) return NULL; @@ -148,6 +150,9 @@ bool condition_test(Condition *c) { case CONDITION_PATH_EXISTS: return (access(c->parameter, F_OK) >= 0) == !c->negate; + case CONDITION_PATH_EXISTS_GLOB: + return (glob_exists(c->parameter) > 0) == !c->negate; + case CONDITION_PATH_IS_DIRECTORY: { struct stat st; @@ -231,6 +236,7 @@ void condition_dump_list(Condition *first, FILE *f, const char *prefix) { static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_PATH_EXISTS] = "ConditionPathExists", + [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", diff --git a/src/condition.h b/src/condition.h index 84028028c4..ff896a793a 100644 --- a/src/condition.h +++ b/src/condition.h @@ -28,6 +28,7 @@ typedef enum ConditionType { CONDITION_PATH_EXISTS, + CONDITION_PATH_EXISTS_GLOB, CONDITION_PATH_IS_DIRECTORY, CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_KERNEL_COMMAND_LINE, diff --git a/src/dbus-common.c b/src/dbus-common.c index 5bfaf361fb..43729f0372 100644 --- a/src/dbus-common.c +++ b/src/dbus-common.c @@ -821,3 +821,153 @@ int bus_append_strv_iter(DBusMessageIter *iter, char **l) { return 0; } + +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { + + assert(iter); + assert(data); + + if (dbus_message_iter_get_arg_type(iter) != type) + return -EIO; + + dbus_message_iter_get_basic(iter, data); + + if (!dbus_message_iter_next(iter) != !next) + return -EIO; + + return 0; +} + +int generic_print_property(const char *name, DBusMessageIter *iter, bool all) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + dbus_message_iter_get_basic(iter, &s); + + if (all || !isempty(s)) + printf("%s=%s\n", name, s); + + return 1; + } + + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + + dbus_message_iter_get_basic(iter, &b); + printf("%s=%s\n", name, yes_no(b)); + + return 1; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + dbus_message_iter_get_basic(iter, &u); + + /* Yes, heuristics! But we can change this check + * should it turn out to not be sufficient */ + + if (endswith(name, "Timestamp")) { + char timestamp[FORMAT_TIMESTAMP_MAX], *t; + + t = format_timestamp(timestamp, sizeof(timestamp), u); + if (t || all) + printf("%s=%s\n", name, strempty(t)); + + } else if (strstr(name, "USec")) { + char timespan[FORMAT_TIMESPAN_MAX]; + + printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u)); + } else + printf("%s=%llu\n", name, (unsigned long long) u); + + return 1; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + dbus_message_iter_get_basic(iter, &u); + + if (strstr(name, "UMask") || strstr(name, "Mode")) + printf("%s=%04o\n", name, u); + else + printf("%s=%u\n", name, (unsigned) u); + + return 1; + } + + case DBUS_TYPE_INT32: { + int32_t i; + dbus_message_iter_get_basic(iter, &i); + + printf("%s=%i\n", name, (int) i); + return 1; + } + + case DBUS_TYPE_DOUBLE: { + double d; + dbus_message_iter_get_basic(iter, &d); + + printf("%s=%g\n", name, d); + return 1; + } + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + DBusMessageIter sub; + bool space = false; + + dbus_message_iter_recurse(iter, &sub); + if (all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + 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("%s%s", space ? " " : "", s); + + space = true; + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 1; + + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + if (all || + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + printf("%s=", name); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + uint8_t u; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE); + dbus_message_iter_get_basic(&sub, &u); + printf("%02x", u); + + dbus_message_iter_next(&sub); + } + + puts(""); + } + + return 1; + } + + break; + } + + return 0; +} diff --git a/src/dbus-common.h b/src/dbus-common.h index 04485e50e4..aab65639c1 100644 --- a/src/dbus-common.h +++ b/src/dbus-common.h @@ -163,4 +163,8 @@ int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l); int bus_append_strv_iter(DBusMessageIter *iter, char **l); +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next); + +int generic_print_property(const char *name, DBusMessageIter *iter, bool all); + #endif diff --git a/src/fdset.c b/src/fdset.c index 9bf3788849..e67fe6fabf 100644 --- a/src/fdset.c +++ b/src/fdset.c @@ -49,6 +49,12 @@ void fdset_free(FDSet *s) { * here, so that the EBADFD that valgrind will return * us on close() doesn't influence us */ + /* When reloading duplicates of the private bus + * connection fds and suchlike are closed here, which + * has no effect at all, since they are only + * duplicates. So don't be surprised about these log + * messages. */ + log_debug("Closing left-over fd %i", PTR_TO_FD(p)); close_nointr(PTR_TO_FD(p)); } diff --git a/src/load-fragment.c b/src/load-fragment.c index 8f39839986..05e60bf8fd 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -1999,12 +1999,13 @@ static int load_from_path(Unit *u, const char *path) { { "IgnoreOnIsolate", config_parse_bool, 0, &u->meta.ignore_on_isolate, "Unit" }, { "IgnoreOnSnapshot", config_parse_bool, 0, &u->meta.ignore_on_snapshot, "Unit" }, { "JobTimeoutSec", config_parse_usec, 0, &u->meta.job_timeout, "Unit" }, - { "ConditionPathExists", config_parse_condition_path, CONDITION_PATH_EXISTS, u, "Unit" }, - { "ConditionPathIsDirectory", config_parse_condition_path, CONDITION_PATH_IS_DIRECTORY, u, "Unit" }, - { "ConditionDirectoryNotEmpty", config_parse_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, u, "Unit" }, + { "ConditionPathExists", config_parse_condition_path, CONDITION_PATH_EXISTS, u, "Unit" }, + { "ConditionPathExistsGlob", config_parse_condition_path, CONDITION_PATH_EXISTS_GLOB, u, "Unit" }, + { "ConditionPathIsDirectory", config_parse_condition_path, CONDITION_PATH_IS_DIRECTORY, u, "Unit" }, + { "ConditionDirectoryNotEmpty", config_parse_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, u, "Unit" }, { "ConditionKernelCommandLine", config_parse_condition_string, CONDITION_KERNEL_COMMAND_LINE, u, "Unit" }, - { "ConditionVirtualization", config_parse_condition_string, CONDITION_VIRTUALIZATION, u, "Unit" }, - { "ConditionSecurity", config_parse_condition_string, CONDITION_SECURITY, u, "Unit" }, + { "ConditionVirtualization", config_parse_condition_string, CONDITION_VIRTUALIZATION, u, "Unit" }, + { "ConditionSecurity", config_parse_condition_string, CONDITION_SECURITY, u, "Unit" }, { "ConditionNull", config_parse_condition_null, 0, u, "Unit" }, { "PIDFile", config_parse_path_printf, 0, &u->service.pid_file, "Service" }, @@ -2094,6 +2095,7 @@ static int load_from_path(Unit *u, const char *path) { { "Unit", config_parse_timer_unit, 0, &u->timer, "Timer" }, { "PathExists", config_parse_path_spec, 0, &u->path, "Path" }, + { "PathExistsGlob", config_parse_path_spec, 0, &u->path, "Path" }, { "PathChanged", config_parse_path_spec, 0, &u->path, "Path" }, { "DirectoryNotEmpty", config_parse_path_spec, 0, &u->path, "Path" }, { "Unit", config_parse_path_unit, 0, &u->path, "Path" }, diff --git a/src/loginctl.c b/src/loginctl.c new file mode 100644 index 0000000000..23c998cba2 --- /dev/null +++ b/src/loginctl.c @@ -0,0 +1,1513 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dbus/dbus.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <pwd.h> + +#include "log.h" +#include "util.h" +#include "macro.h" +#include "pager.h" +#include "dbus-common.h" +#include "build.h" +#include "strv.h" +#include "cgroup-show.h" +#include "sysfs-show.h" + +static char **arg_property = NULL; +static bool arg_all = false; +static bool arg_no_pager = false; +static const char *arg_kill_who = NULL; +static int arg_signal = SIGTERM; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static const char *arg_host = NULL; + +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) { + on_tty(); + + if (!arg_no_pager) + pager_open(); +} + +static int list_sessions(DBusConnection *bus, char **args, unsigned n) { + 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(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSessions"); + 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); + + if (on_tty()) + printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *id, *user, *seat, *object; + uint32_t uid; + + 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_UINT32, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u sessions 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 list_users(DBusConnection *bus, char **args, unsigned n) { + 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(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListUsers"); + 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); + + if (on_tty()) + printf("%10s %-16s\n", "UID", "USER"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *user, *object; + uint32_t uid; + + 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, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%10u %-16s\n", (unsigned) uid, user); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u users 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 list_seats(DBusConnection *bus, char **args, unsigned n) { + 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(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats"); + 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); + + if (on_tty()) + printf("%-16s\n", "SEAT"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *seat, *object; + + 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, &seat, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%-16s\n", seat); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u seats listed.\n", k); + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +typedef struct SessionStatusInfo { + const char *id; + uid_t uid; + const char *name; + usec_t timestamp; + const char *control_group; + int vtnr; + const char *seat; + const char *tty; + const char *display; + bool remote; + const char *remote_host; + const char *remote_user; + const char *service; + pid_t leader; + const char *type; + bool active; +} SessionStatusInfo; + +typedef struct UserStatusInfo { + uid_t uid; + const char *name; + usec_t timestamp; + const char *control_group; + const char *state; + char **sessions; + const char *display; +} UserStatusInfo; + +typedef struct SeatStatusInfo { + const char *id; + const char *active_session; + char **sessions; +} SeatStatusInfo; + +static void print_session_status_info(SessionStatusInfo *i) { + char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + assert(i); + + printf("%s - ", strna(i->id)); + + if (i->name) + printf("%s (%u)\n", i->name, (unsigned) i->uid); + else + printf("%u\n", (unsigned) i->uid); + + s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp); + s2 = format_timestamp(since2, sizeof(since2), i->timestamp); + + if (s1) + printf("\t Since: %s; %s\n", s2, s1); + else if (s2) + printf("\t Since: %s\n", s2); + + if (i->leader > 0) { + char *t = NULL; + + printf("\t Leader: %u", (unsigned) i->leader); + + get_process_name(i->leader, &t); + if (t) { + printf(" (%s)", t); + free(t); + } + + printf("\n"); + } + + if (i->seat) { + printf("\t Seat: %s", i->seat); + + if (i->vtnr > 0) + printf("; vc%i", i->vtnr); + + printf("\n"); + } + + if (i->tty) + printf("\t TTY: %s\n", i->tty); + else if (i->display) + printf("\t Display: %s\n", i->display); + + if (i->remote_host && i->remote_user) + printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host); + else if (i->remote_host) + printf("\t Remote: %s\n", i->remote_host); + else if (i->remote_user) + printf("\t Remote: user %s\n", i->remote_user); + else if (i->remote) + printf("\t Remote: Yes\n"); + + if (i->service) { + printf("\t Service: %s", i->service); + + if (i->type) + printf("; type %s", i->type); + + printf("\n"); + } else if (i->type) + printf("\t Type: %s\n", i->type); + + printf("\t Active: %s\n", yes_no(i->active)); + + if (i->control_group) { + unsigned c; + + printf("\t CGroup: %s\n", i->control_group); + + if (arg_transport != TRANSPORT_SSH) { + c = columns(); + if (c > 18) + c -= 18; + else + c = 0; + + show_cgroup_by_path(i->control_group, "\t\t ", c); + } + } +} + +static void print_user_status_info(UserStatusInfo *i) { + char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; + char since2[FORMAT_TIMESTAMP_MAX], *s2; + assert(i); + + if (i->name) + printf("%s (%u)\n", i->name, (unsigned) i->uid); + else + printf("%u\n", (unsigned) i->uid); + + s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp); + s2 = format_timestamp(since2, sizeof(since2), i->timestamp); + + if (s1) + printf("\t Since: %s; %s\n", s2, s1); + else if (s2) + printf("\t Since: %s\n", s2); + + if (!isempty(i->state)) + printf("\t State: %s\n", i->state); + + if (!strv_isempty(i->sessions)) { + char **l; + printf("\tSessions:"); + + STRV_FOREACH(l, i->sessions) { + if (streq_ptr(*l, i->display)) + printf(" *%s", *l); + else + printf(" %s", *l); + } + + printf("\n"); + } + + if (i->control_group) { + unsigned c; + + printf("\t CGroup: %s\n", i->control_group); + + if (arg_transport != TRANSPORT_SSH) { + c = columns(); + if (c > 18) + c -= 18; + else + c = 0; + + show_cgroup_by_path(i->control_group, "\t\t ", c); + } + } +} + +static void print_seat_status_info(SeatStatusInfo *i) { + assert(i); + + printf("%s\n", strna(i->id)); + + if (!strv_isempty(i->sessions)) { + char **l; + printf("\tSessions:"); + + STRV_FOREACH(l, i->sessions) { + if (streq_ptr(*l, i->active_session)) + printf(" *%s", *l); + else + printf(" %s", *l); + } + + printf("\n"); + } + + if (arg_transport != TRANSPORT_SSH) { + unsigned c; + + c = columns(); + if (c > 18) + c -= 18; + else + c = 0; + + printf("\t Devices:\n"); + + + show_sysfs(i->id, "\t\t ", c); + } +} + +static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *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, "Name")) + i->name = s; + else if (streq(name, "ControlGroupPath")) + i->control_group = s; + else if (streq(name, "TTY")) + i->tty = s; + else if (streq(name, "Display")) + i->display = s; + else if (streq(name, "RemoteHost")) + i->remote_host = s; + else if (streq(name, "RemoteUser")) + i->remote_user = s; + else if (streq(name, "Service")) + i->service = s; + else if (streq(name, "Type")) + i->type = s; + } + break; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "VTNr")) + i->vtnr = (int) u; + else if (streq(name, "Leader")) + i->leader = (pid_t) u; + + break; + } + + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + + dbus_message_iter_get_basic(iter, &b); + + if (streq(name, "Remote")) + i->remote = b; + else if (streq(name, "Active")) + i->active = b; + + break; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "Timestamp")) + i->timestamp = (usec_t) u; + + break; + } + + 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, "User")) { + uint32_t u; + + dbus_message_iter_get_basic(&sub, &u); + i->uid = (uid_t) u; + + } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) { + const char *s; + + dbus_message_iter_get_basic(&sub, &s); + + if (!isempty(s)) + i->seat = s; + } + + break; + } + } + + return 0; +} + +static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *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, "Name")) + i->name = s; + else if (streq(name, "ControlGroupPath")) + i->control_group = s; + else if (streq(name, "State")) + i->state = s; + } + break; + } + + case DBUS_TYPE_UINT32: { + uint32_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "UID")) + i->uid = (uid_t) u; + + break; + } + + case DBUS_TYPE_UINT64: { + uint64_t u; + + dbus_message_iter_get_basic(iter, &u); + + if (streq(name, "Timestamp")) + i->timestamp = (usec_t) u; + + break; + } + + case DBUS_TYPE_STRUCT: { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) { + const char *s; + + dbus_message_iter_get_basic(&sub, &s); + + if (!isempty(s)) + i->display = s; + } + + break; + } + + case DBUS_TYPE_ARRAY: { + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *id; + const char *path; + + 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_OBJECT_PATH, &path, false) >= 0) { + char **l; + + l = strv_append(i->sessions, id); + if (!l) + return -ENOMEM; + + strv_free(i->sessions); + i->sessions = l; + } + + dbus_message_iter_next(&sub); + } + + return 0; + } + } + } + + return 0; +} + +static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *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; + } + break; + } + + case DBUS_TYPE_STRUCT: { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) { + const char *s; + + dbus_message_iter_get_basic(&sub, &s); + + if (!isempty(s)) + i->active_session = s; + } + + break; + } + + case DBUS_TYPE_ARRAY: { + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { + DBusMessageIter sub, sub2; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *id; + const char *path; + + 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_OBJECT_PATH, &path, false) >= 0) { + char **l; + + l = strv_append(i->sessions, id); + if (!l) + return -ENOMEM; + + strv_free(i->sessions); + i->sessions = l; + } + + dbus_message_iter_next(&sub); + } + + return 0; + } + } + } + + return 0; +} + +static int print_property(const char *name, DBusMessageIter *iter) { + assert(name); + assert(iter); + + 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_STRING && + (streq(name, "Display") || streq(name, "ActiveSession"))) { + const char *s; + + dbus_message_iter_get_basic(&sub, &s); + + if (arg_all || !isempty(s)) + printf("%s=%s\n", name, s); + return 0; + } + break; + } + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) { + DBusMessageIter sub, sub2; + bool found = false; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) { + const char *id; + const char *path; + + 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_OBJECT_PATH, &path, false) >= 0) { + if (found) + printf(" %s", id); + else { + printf("%s=%s", name, id); + found = true; + } + } + + dbus_message_iter_next(&sub); + } + + if (!found && arg_all) + printf("%s=\n", name); + else if (found) + printf("\n"); + + 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; + SessionStatusInfo session_info; + UserStatusInfo user_info; + SeatStatusInfo seat_info; + + assert(bus); + assert(path); + assert(new_line); + + zero(session_info); + zero(user_info); + zero(seat_info); + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + path, + "org.freedesktop.DBus.Properties", + "GetAll"); + if (!m) { + 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; + } + + 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_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 if (strstr(verb, "session")) + r = status_property_session(name, &sub3, &session_info); + else if (strstr(verb, "user")) + r = status_property_user(name, &sub3, &user_info); + else + r = status_property_seat(name, &sub3, &seat_info); + + if (r < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_next(&sub); + } + + if (!show_properties) { + if (strstr(verb, "session")) + print_session_status_info(&session_info); + else if (strstr(verb, "user")) + print_user_status_info(&user_info); + else + print_seat_status_info(&seat_info); + } + + strv_free(seat_info.sessions); + strv_free(user_info.sessions); + + r = 0; + +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, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + int r, ret = 0; + DBusError error; + unsigned i; + bool show_properties, new_line = false; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + show_properties = !strstr(args[0], "status"); + + if (show_properties) + pager_open_if_enabled(); + + if (show_properties && n <= 1) { + /* If not argument is specified inspect the manager + * itself */ + + ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line); + goto finish; + } + + for (i = 1; i < n; i++) { + const char *path = NULL; + + if (strstr(args[0], "session")) { + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetSession"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -ENOMEM; + goto finish; + } + + } else if (strstr(args[0], "user")) { + uint32_t uid; + + if (safe_atou(args[i], &uid) < 0) { + struct passwd *pw; + + pw = getpwnam(args[i]); + if (!pw) { + log_error("User %s unknown.", args[i]); + ret = -ENOENT; + goto finish; + } + + uid = pw->pw_uid; + } + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetUser"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_UINT32, &uid, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -ENOMEM; + goto finish; + } + } else { + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "GetSeat"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -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)); + 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; + } + + r = show_one(args[0], bus, path, show_properties, &new_line); + if (r != 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 activate(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + int ret = 0; + DBusError error; + unsigned i; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + for (i = 1; i < n; i++) { + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ActivateSession"); + if (!m) { + log_error("Could not allocate message."); + ret = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + ret = -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)); + ret = -EIO; + goto finish; + } + + 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 kill_session(DBusConnection *bus, char **args, unsigned n) { + return 0; +} + +static int enable_linger(DBusConnection *bus, char **args, unsigned n) { + return 0; +} + +static int attach(DBusConnection *bus, char **args, unsigned n) { + return 0; +} + +static int flush_devices(DBusConnection *bus, char **args, unsigned n) { + return 0; +} + +static int terminate_seat(DBusConnection *bus, char **args, unsigned n) { + return 0; +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the login manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -p --property=NAME Show only properties by this name\n" + " -a --all Show all properties, including empty ones\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" + " --no-pager Do not pipe output into a pager\n\n" + "Commands:\n" + " list-sessions List sessions\n" + " session-status [ID...] Show session status\n" + " show-session [ID...] Show property of one or more sessions\n" + " activate [ID] Activate a session\n" + " lock-session [ID...] Screen lock one or more sessions\n" + " terminate-session [ID...] Terminate one or more sessions\n" + " kill-session [ID...] Send signal to processes of a session\n" + " list-users List users\n" + " user-status [USER...] Show user status\n" + " show-user [USER...] Show property of one or more users\n" + " enable-linger [USER...] Enable linger state of one or more users\n" + " disable-linger [USER...] Disable linger state of one or more users\n" + " terminate-user [USER...] Terminate all sessions of one or more users\n" + " kill-user [USER...] Send signal to processes of a user\n" + " list-seats List seats\n" + " seat-status [NAME...] Show seat status\n" + " show-seat [NAME...] Show property of one or more seats\n" + " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n" + " flush-devices Flush all device associations\n" + " terminate-seat [NAME...] Terminate all sessions on one or more seats\n" + " kill-seat [NAME...] Send signal to processes of sessions on a seat\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_KILL_WHO + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "host", required_argument, NULL, 'H' }, + { "privileged",no_argument, NULL, 'P' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'p': { + char **l; + + l = strv_append(arg_property, optarg); + if (!l) + 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_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_KILL_WHO: + arg_kill_who = optarg; + break; + + case 's': + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { + log_error("Failed to parse signal string %s.", optarg); + return -EINVAL; + } + break; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int loginctl_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, unsigned n); + } verbs[] = { + { "list-sessions", LESS, 1, list_sessions }, + { "session-status", MORE, 2, show }, + { "show-session", MORE, 1, show }, + { "activate", EQUAL, 2, activate }, + { "lock-session", MORE, 2, activate }, + { "terminate-session", MORE, 2, activate }, + { "kill-session", MORE, 2, kill_session }, + { "list-users", EQUAL, 1, list_users }, + { "user-status", MORE, 2, show }, + { "show-user", MORE, 1, show }, + { "enable-linger", MORE, 2, enable_linger }, + { "disable-linger", MORE, 2, enable_linger }, + { "terminate-user", MORE, 2, enable_linger }, + { "kill-user", MORE, 2, kill_session }, + { "list-seats", EQUAL, 1, list_seats }, + { "seat-status", MORE, 2, show }, + { "show-seat", MORE, 1, show }, + { "attach", MORE, 3, attach }, + { "flush-devices", EQUAL, 1, flush_devices }, + { "terminate-seat", MORE, 2, terminate_seat }, + { "kill-seat", MORE, 2, kill_session }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "list-sessions" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + 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."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +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; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = loginctl_main(bus, argc, argv, &error); + 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(); + + return retval; +} diff --git a/src/logind-acl.c b/src/logind-acl.c index 59af981a5e..2aa5866ccd 100644 --- a/src/logind-acl.c +++ b/src/logind-acl.c @@ -229,18 +229,16 @@ int devnode_acl_all(struct udev *udev, if (!e) return -ENOMEM; + /* We can only match by one tag in libudev. We choose + * "uaccess" for that. If we could match for two tags here we + * could add the seat name as second match tag, but this would + * be hardly optimizable in libudev, and hence checking the + * second tag manually in our loop is a good solution. */ + r = udev_enumerate_add_match_tag(e, "uaccess"); if (r < 0) goto finish; - /* FIXME: when libudev is able to handle multiple match tags - * properly, optimize the search here a bit */ - /* if (!streq(seat, "seat0")) { */ - /* r = udev_enumerate_add_match_tag(e, seat); */ - /* if (r < 0) */ - /* goto finish; */ - /* } */ - r = udev_enumerate_scan_devices(e); if (r < 0) goto finish; diff --git a/src/logind-acl.h b/src/logind-acl.h index 9c88a80644..72740f5b95 100644 --- a/src/logind-acl.h +++ b/src/logind-acl.h @@ -26,6 +26,8 @@ #include <stdbool.h> #include <libudev.h> +#ifdef HAVE_ACL + int devnode_acl(const char *path, bool flush, bool del, uid_t old_uid, @@ -36,5 +38,23 @@ int devnode_acl_all(struct udev *udev, bool flush, bool del, uid_t old_uid, bool add, uid_t new_uid); +#else + +static inline int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +static inline int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + return 0; +} + +#endif #endif diff --git a/src/logind-dbus.c b/src/logind-dbus.c index 96216e43a3..a2a442ee27 100644 --- a/src/logind-dbus.c +++ b/src/logind-dbus.c @@ -541,6 +541,7 @@ static bool device_has_tag(struct udev_device *d, const char *tag) { assert(d); assert(tag); + /* FIXME */ udev_device_get_is_initialized(d); first = udev_device_get_tags_list_entry(d); @@ -634,7 +635,7 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) { } mkdir_p("/etc/udev/rules.d", 0755); - r = write_one_line_file(file, rule); + r = write_one_line_file_atomic(file, rule); if (r < 0) goto finish; diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c index 0ef6558851..eca33e5e24 100644 --- a/src/logind-session-dbus.c +++ b/src/logind-session-dbus.c @@ -36,7 +36,7 @@ " <method name=\"SetIdleHint\">\n" \ " <arg name=\"b\" type=\"b\"/>\n" \ " </method>\n" \ - " <property name=\"Id\" type=\"u\" access=\"read\"/>\n" \ + " <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n" \ " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \ diff --git a/src/logind.c b/src/logind.c index cbc878e682..f96ace2315 100644 --- a/src/logind.c +++ b/src/logind.c @@ -451,7 +451,6 @@ static int manager_enumerate_users_from_cgroup(Manager *m) { return r; } - static int manager_enumerate_linger_users(Manager *m) { DIR *d; struct dirent *de; @@ -564,6 +563,9 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) { while ((k = cg_read_subgroup(d, &name)) > 0) { Session *session; + if (streq(name, "shared")) + continue; + k = manager_add_session(m, u, name, &session); if (k < 0) { free(name); diff --git a/src/main.c b/src/main.c index 76a0943832..0bcd09c4cf 100644 --- a/src/main.c +++ b/src/main.c @@ -898,6 +898,9 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) { assert(_f); assert(_fds); + /* Make sure nothing is really destructed when we shut down */ + m->n_reloading ++; + if ((r = manager_open_serialization(m, &f)) < 0) { log_error("Failed to create serialization file: %s", strerror(-r)); goto fail; @@ -988,7 +991,7 @@ static void test_usr(void) { if (dir_is_empty("/usr") <= 0) return; - log_warning("/usr appears to be on a different file system than /. This is not supported anymore. " + log_warning("/usr appears to be on its own filesytem and is not already mounted. This is not a supported setup. " "Some things will probably break (sometimes even silently) in mysterious ways. " "Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information."); } diff --git a/src/manager.c b/src/manager.c index 258b3ca4b7..3291275d0a 100644 --- a/src/manager.c +++ b/src/manager.c @@ -136,7 +136,7 @@ static int enable_special_signals(Manager *m) { if (reboot(RB_DISABLE_CAD) < 0) log_warning("Failed to enable ctrl-alt-del handling: %m"); - if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY)) < 0) + if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0) log_warning("Failed to open /dev/tty0: %m"); else { /* Enable that we get SIGWINCH on kbrequest */ @@ -595,7 +595,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { * this is already known, so we increase the counter here * already */ if (serialization) - m->n_deserializing ++; + m->n_reloading ++; /* First, enumerate what we can from all config files */ r = manager_enumerate(m); @@ -610,8 +610,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { r = q; if (serialization) { - assert(m->n_deserializing > 0); - m->n_deserializing --; + assert(m->n_reloading > 0); + m->n_reloading --; } return r; @@ -2476,7 +2476,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { /* Don't generate audit events if the service was already * started and we're just deserializing */ - if (m->n_deserializing > 0) + if (m->n_reloading > 0) return; if (m->running_as != MANAGER_SYSTEM) @@ -2517,7 +2517,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { /* Don't generate plymouth events if the service was already * started and we're just deserializing */ - if (m->n_deserializing > 0) + if (m->n_reloading > 0) return; if (m->running_as != MANAGER_SYSTEM) @@ -2659,7 +2659,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { assert(f); assert(fds); - m->n_serializing ++; + m->n_reloading ++; fprintf(f, "current-job-id=%i\n", m->current_job_id); fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); @@ -2682,13 +2682,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { fputc('\n', f); if ((r = unit_serialize(u, f, fds)) < 0) { - m->n_serializing --; + m->n_reloading --; return r; } } - assert(m->n_serializing > 0); - m->n_serializing --; + assert(m->n_reloading > 0); + m->n_reloading --; if (ferror(f)) return -EIO; @@ -2708,7 +2708,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { log_debug("Deserializing state..."); - m->n_deserializing ++; + m->n_reloading ++; for (;;) { char line[LINE_MAX], *l; @@ -2781,8 +2781,8 @@ finish: goto finish; } - assert(m->n_deserializing > 0); - m->n_deserializing --; + assert(m->n_reloading > 0); + m->n_reloading --; return r; } @@ -2797,21 +2797,21 @@ int manager_reload(Manager *m) { if ((r = manager_open_serialization(m, &f)) < 0) return r; - m->n_serializing ++; + m->n_reloading ++; if (!(fds = fdset_new())) { - m->n_serializing --; + m->n_reloading --; r = -ENOMEM; goto finish; } if ((r = manager_serialize(m, f, fds)) < 0) { - m->n_serializing --; + m->n_reloading --; goto finish; } if (fseeko(f, 0, SEEK_SET) < 0) { - m->n_serializing --; + m->n_reloading --; r = -errno; goto finish; } @@ -2820,9 +2820,6 @@ int manager_reload(Manager *m) { manager_clear_jobs_and_units(m); manager_undo_generators(m); - assert(m->n_serializing > 0); - m->n_serializing --; - /* Find new unit paths */ lookup_paths_free(&m->lookup_paths); if ((q = lookup_paths_init(&m->lookup_paths, m->running_as)) < 0) @@ -2832,8 +2829,6 @@ int manager_reload(Manager *m) { manager_build_unit_path_cache(m); - m->n_deserializing ++; - /* First, enumerate what we can from all config files */ if ((q = manager_enumerate(m)) < 0) r = q; @@ -2849,8 +2844,8 @@ int manager_reload(Manager *m) { if ((q = manager_coldplug(m)) < 0) r = q; - assert(m->n_deserializing > 0); - m->n_deserializing--; + assert(m->n_reloading > 0); + m->n_reloading--; finish: if (f) diff --git a/src/manager.h b/src/manager.h index 4557d5f0d5..22730d2176 100644 --- a/src/manager.h +++ b/src/manager.h @@ -223,8 +223,8 @@ struct Manager { ExecOutput default_std_output, default_std_error; - int n_serializing; - int n_deserializing; + /* non-zero if we are reloading or reexecuting, */ + int n_reloading; unsigned n_installed_jobs; unsigned n_failed_jobs; diff --git a/src/org.freedesktop.hostname1.policy b/src/org.freedesktop.hostname1.policy.in index 0ad64d9ee1..7d56b22c28 100644 --- a/src/org.freedesktop.hostname1.policy +++ b/src/org.freedesktop.hostname1.policy.in @@ -17,8 +17,8 @@ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> <action id="org.freedesktop.hostname1.set-hostname"> - <description>Set host name</description> - <message>Authentication is required to set the local host name.</message> + <_description>Set host name</_description> + <_message>Authentication is required to set the local host name.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -27,8 +27,8 @@ </action> <action id="org.freedesktop.hostname1.set-static-hostname"> - <description>Set static host name</description> - <message>Authentication is required to set the statically configured local host name, as well as the pretty host name.</message> + <_description>Set static host name</_description> + <_message>Authentication is required to set the statically configured local host name, as well as the pretty host name.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -37,8 +37,8 @@ </action> <action id="org.freedesktop.hostname1.set-machine-info"> - <description>Set machine information</description> - <message>Authentication is required to set local machine information.</message> + <_description>Set machine information</_description> + <_message>Authentication is required to set local machine information.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> diff --git a/src/org.freedesktop.locale1.policy b/src/org.freedesktop.locale1.policy.in index 6c755fd6b7..186d7d34c5 100644 --- a/src/org.freedesktop.locale1.policy +++ b/src/org.freedesktop.locale1.policy.in @@ -17,8 +17,8 @@ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> <action id="org.freedesktop.locale1.set-locale"> - <description>Set system locale</description> - <message>Authentication is required to set the system locale.</message> + <_description>Set system locale</_description> + <_message>Authentication is required to set the system locale.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> diff --git a/src/org.freedesktop.login1.policy b/src/org.freedesktop.login1.policy.in index 7ad8f6f316..9482c87f1f 100644 --- a/src/org.freedesktop.login1.policy +++ b/src/org.freedesktop.login1.policy.in @@ -17,8 +17,8 @@ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> <action id="org.freedesktop.login1.set-user-linger"> - <description>Allow non-logged-in users to run programs</description> - <message>Authentication is required to allow a non-logged-in user to run programs</message> + <_description>Allow non-logged-in users to run programs</_description> + <_message>Authentication is required to allow a non-logged-in user to run programs</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -27,8 +27,8 @@ </action> <action id="org.freedesktop.login1.attach-device"> - <description>Allow attaching devices to seats</description> - <message>Authentication is required to allow attaching a device to a seat</message> + <_description>Allow attaching devices to seats</_description> + <_message>Authentication is required to allow attaching a device to a seat</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -37,8 +37,8 @@ </action> <action id="org.freedesktop.login1.flush-devices"> - <description>Flush device to seat attachments</description> - <message>Authentication is required to allow reseting how devices are attached to seats</message> + <_description>Flush device to seat attachments</_description> + <_message>Authentication is required to allow reseting how devices are attached to seats</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> diff --git a/src/org.freedesktop.timedate1.policy b/src/org.freedesktop.timedate1.policy.in index 5010efd6c5..f73e1aa7e0 100644 --- a/src/org.freedesktop.timedate1.policy +++ b/src/org.freedesktop.timedate1.policy.in @@ -17,8 +17,8 @@ <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> <action id="org.freedesktop.timedate1.set-time"> - <description>Set system time</description> - <message>Authentication is required to set the system time.</message> + <_description>Set system time</_description> + <_message>Authentication is required to set the system time.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -27,8 +27,8 @@ </action> <action id="org.freedesktop.timedate1.set-timezone"> - <description>Set system timezone</description> - <message>Authentication is required to set the system timezone.</message> + <_description>Set system timezone</_description> + <_message>Authentication is required to set the system timezone.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> @@ -37,9 +37,9 @@ </action> <action id="org.freedesktop.timedate1.set-local-rtc"> - <description>Set RTC to local timezone or UTC</description> - <message>Authentication is required to control whether - the RTC stores the local or UTC time.</message> + <_description>Set RTC to local timezone or UTC</_description> + <_message>Authentication is required to control whether + the RTC stores the local or UTC time.</_message> <defaults> <allow_any>auth_admin_keep</allow_any> <allow_inactive>auth_admin_keep</allow_inactive> diff --git a/src/pager.c b/src/pager.c new file mode 100644 index 0000000000..6ea25ada65 --- /dev/null +++ b/src/pager.c @@ -0,0 +1,122 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/prctl.h> + +#include "pager.h" +#include "util.h" +#include "macro.h" + +static pid_t pager_pid = 0; + +void pager_open(void) { + int fd[2]; + const char *pager; + pid_t parent_pid; + + if (pager_pid > 0) + return; + + if (isatty(STDOUT_FILENO) <= 0) + return; + + if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER"))) + if (!*pager || streq(pager, "cat")) + return; + + /* Determine and cache number of columns before we spawn the + * pager so that we get the value from the actual tty */ + columns(); + + if (pipe(fd) < 0) { + log_error("Failed to create pager pipe: %m"); + return; + } + + parent_pid = getpid(); + + pager_pid = fork(); + if (pager_pid < 0) { + log_error("Failed to fork pager: %m"); + close_pipe(fd); + return; + } + + /* In the child start the pager */ + if (pager_pid == 0) { + + dup2(fd[0], STDIN_FILENO); + close_pipe(fd); + + setenv("LESS", "FRSX", 0); + + /* Make sure the pager goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + if (pager) { + execlp(pager, pager, NULL); + execl("/bin/sh", "sh", "-c", pager, NULL); + } + + /* Debian's alternatives command for pagers is + * called 'pager'. Note that we do not call + * sensible-pagers here, since that is just a + * shell script that implements a logic that + * is similar to this one anyway, but is + * Debian-specific. */ + execlp("pager", "pager", NULL); + + execlp("less", "less", NULL); + execlp("more", "more", NULL); + execlp("cat", "cat", NULL); + + log_error("Unable to execute pager: %m"); + _exit(EXIT_FAILURE); + } + + /* Return in the parent */ + if (dup2(fd[1], STDOUT_FILENO) < 0) + log_error("Failed to duplicate pager pipe: %m"); + + close_pipe(fd); +} + +void pager_close(void) { + + if (pager_pid <= 0) + return; + + /* Inform pager that we are done */ + fclose(stdout); + kill(pager_pid, SIGCONT); + wait_for_terminate(pager_pid, NULL); + pager_pid = 0; +} diff --git a/src/pager.h b/src/pager.h new file mode 100644 index 0000000000..b5b4998445 --- /dev/null +++ b/src/pager.h @@ -0,0 +1,28 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foopagerhfoo +#define foopagerhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +void pager_open(void); +void pager_close(void); + +#endif diff --git a/src/path.c b/src/path.c index 1c20dcfed6..200fc2bdcb 100644 --- a/src/path.c +++ b/src/path.c @@ -197,6 +197,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { static int path_watch_one(Path *p, PathSpec *s) { static const int flags_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, + [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO }; @@ -367,6 +368,10 @@ static bool path_check_good(Path *p, bool initial) { good = access(s->path, F_OK) >= 0; break; + case PATH_EXISTS_GLOB: + good = glob_exists(s->path) > 0; + break; + case PATH_DIRECTORY_NOT_EMPTY: { int k; @@ -438,7 +443,7 @@ static void path_mkdir(Path *p) { LIST_FOREACH(spec, s, p->specs) { int r; - if (s->type == PATH_EXISTS) + if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) continue; if ((r = mkdir_p(s->path, p->directory_mode)) < 0) @@ -672,6 +677,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_state, PathState); static const char* const path_type_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = "PathExists", + [PATH_EXISTS_GLOB] = "PathExistsGlob", [PATH_CHANGED] = "PathChanged", [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" }; diff --git a/src/path.h b/src/path.h index 8ba0ce6890..116fc63ff7 100644 --- a/src/path.h +++ b/src/path.h @@ -38,6 +38,7 @@ typedef enum PathState { typedef enum PathType { PATH_EXISTS, + PATH_EXISTS_GLOB, PATH_DIRECTORY_NOT_EMPTY, PATH_CHANGED, _PATH_TYPE_MAX, diff --git a/src/service.c b/src/service.c index 165655eb12..b684a37cd7 100644 --- a/src/service.c +++ b/src/service.c @@ -843,7 +843,7 @@ static int service_load_sysv_path(Service *s, const char *path) { /* Special setting for all SysV services */ s->type = SERVICE_FORKING; - s->remain_after_exit = true; + s->remain_after_exit = !s->pid_file; s->restart = SERVICE_RESTART_NO; s->exec_context.std_output = (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY) @@ -1496,7 +1496,7 @@ static void service_set_state(Service *s, ServiceState state) { /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && s->meta.manager->n_deserializing <= 0) + if (state == SERVICE_EXITED && s->meta.manager->n_reloading <= 0) cgroup_bonding_trim_list(s->meta.cgroup_bondings, true); if (old_state != state) diff --git a/src/snapshot.c b/src/snapshot.c index 9825f90521..270dc4f252 100644 --- a/src/snapshot.c +++ b/src/snapshot.c @@ -66,7 +66,7 @@ static int snapshot_load(Unit *u) { /* Make sure that only snapshots created via snapshot_create() * can be loaded */ - if (!s->by_snapshot_create && s->meta.manager->n_deserializing <= 0) + if (!s->by_snapshot_create && s->meta.manager->n_reloading <= 0) return -ENOENT; u->meta.load_state = UNIT_LOADED; diff --git a/src/sysfs-show.c b/src/sysfs-show.c new file mode 100644 index 0000000000..d12d3ccfc9 --- /dev/null +++ b/src/sysfs-show.c @@ -0,0 +1,191 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <string.h> +#include <libudev.h> + +#include "util.h" +#include "sysfs-show.h" + +static bool device_has_tag(struct udev_device *d, const char *tag) { + struct udev_list_entry *first, *item; + + assert(d); + assert(tag); + + /* FIXME */ + udev_device_get_is_initialized(d); + + first = udev_device_get_tags_list_entry(d); + udev_list_entry_foreach(item, first) + if (streq(udev_list_entry_get_name(item), tag)) + return true; + + return false; +} + +static int show_sysfs_one( + struct udev *udev, + const char *seat, + struct udev_list_entry **item, + const char *sub, + const char *prefix, + unsigned n_columns) { + + assert(udev); + assert(seat); + assert(item); + assert(prefix); + + while (*item) { + struct udev_list_entry *next, *lookahead; + struct udev_device *d; + const char *sn, *id, *name, *sysfs, *subsystem, *sysname; + + sysfs = udev_list_entry_get_name(*item); + if (!path_startswith(sysfs, sub)) + return 0; + + d = udev_device_new_from_syspath(udev, sysfs); + if (!d) { + *item = udev_list_entry_get_next(*item); + continue; + } + + sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(sn)) + sn = "seat0"; + + /* fixme, also check for tag 'seat' here */ + if (!streq(seat, sn) || !device_has_tag(d, "seat")) { + udev_device_unref(d); + *item = udev_list_entry_get_next(*item); + continue; + } + + id = udev_device_get_property_value(d, "ID_FOR_SEAT"); + name = udev_device_get_sysattr_value(d, "name"); + if (!name) + name = udev_device_get_sysattr_value(d, "id"); + subsystem = udev_device_get_subsystem(d); + sysname = udev_device_get_sysname(d); + + /* Look if there's more coming after this */ + lookahead = next = udev_list_entry_get_next(*item); + while (lookahead) { + const char *lookahead_sysfs; + + lookahead_sysfs = udev_list_entry_get_name(lookahead); + + if (path_startswith(lookahead_sysfs, sub) && + !path_startswith(lookahead_sysfs, sysfs)) { + struct udev_device *lookahead_d; + + lookahead_d = udev_device_new_from_syspath(udev, lookahead_sysfs); + if (lookahead_d) { + const char *lookahead_sn; + bool found; + + lookahead_sn = udev_device_get_property_value(d, "ID_SEAT"); + if (isempty(lookahead_sn)) + lookahead_sn = "seat0"; + + found = streq(seat, lookahead_sn) && device_has_tag(d, "seat"); + udev_device_unref(lookahead_d); + + if (found) + break; + } + } + + lookahead = udev_list_entry_get_next(lookahead); + } + + printf("%s%s %s (%s:%s)", prefix, lookahead ? "\342\224\234" : "\342\224\224", id ? id : sysfs, subsystem, sysname); + + if (name) + printf(" \"%s\"\n", name); + else + printf("\n"); + + *item = next; + if (*item) { + char *p; + + p = strappend(prefix, lookahead ? "\342\224\202 " : " "); + show_sysfs_one(udev, seat, item, sysfs, p, n_columns - 2); + free(p); + } + + udev_device_unref(d); + } + + return 0; +} + +int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { + struct udev *udev; + struct udev_list_entry *first = NULL; + struct udev_enumerate *e; + int r; + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if (isempty(seat)) + seat = "seat0"; + + udev = udev_new(); + if (!udev) + return -ENOMEM; + + e = udev_enumerate_new(udev); + if (!e) { + r = -ENOMEM; + goto finish; + } + + if (!streq(seat, "seat0")) + r = udev_enumerate_add_match_tag(e, seat); + else + r = udev_enumerate_add_match_tag(e, "seat"); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + goto finish; + + first = udev_enumerate_get_list_entry(e); + if (first) + show_sysfs_one(udev, seat, &first, "/", prefix, n_columns); + +finish: + if (e) + udev_enumerate_unref(e); + + if (udev) + udev_unref(udev); + + return r; +} diff --git a/src/sysfs-show.h b/src/sysfs-show.h new file mode 100644 index 0000000000..9939e8b069 --- /dev/null +++ b/src/sysfs-show.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosysfsshowhfoo +#define foosysfsshowhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +int show_sysfs(const char *seat, const char *prefix, unsigned columns); + +#endif diff --git a/src/systemctl.c b/src/systemctl.c index 8f904c16c2..840e2ffc2d 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -33,7 +33,6 @@ #include <sys/stat.h> #include <stddef.h> #include <sys/prctl.h> - #include <dbus/dbus.h> #include "log.h" @@ -56,6 +55,7 @@ #include "bus-errors.h" #include "build.h" #include "unit-name.h" +#include "pager.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -80,8 +80,8 @@ static bool arg_failed = false; static char **arg_wall = NULL; static const char *arg_kill_who = NULL; static const char *arg_kill_mode = NULL; -static const char *arg_root = NULL; static int arg_signal = SIGTERM; +static const char *arg_root = NULL; static usec_t arg_when = 0; static enum action { ACTION_INVALID, @@ -118,11 +118,9 @@ static const char *arg_host = NULL; static bool private_bus = false; -static pid_t pager_pid = 0; static pid_t agent_pid = 0; static int daemon_reload(DBusConnection *bus, char **args, unsigned n); -static void pager_open(void); static bool on_tty(void) { static int t = -1; @@ -139,6 +137,13 @@ static bool on_tty(void) { return t; } +static void pager_open_if_enabled(void) { + on_tty(); + + if (!arg_no_pager) + pager_open(); +} + static void spawn_ask_password_agent(void) { pid_t parent; @@ -274,22 +279,6 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) { return EXIT_FAILURE; } -static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { - - assert(iter); - assert(data); - - if (dbus_message_iter_get_arg_type(iter) != type) - return -EIO; - - dbus_message_iter_get_basic(iter, data); - - if (!dbus_message_iter_next(iter) != !next) - return -EIO; - - return 0; -} - static void warn_wall(enum action action) { static const char *table[_ACTION_MAX] = { [ACTION_HALT] = "The system is going down for system halt NOW!", @@ -475,7 +464,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { assert(bus); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -796,7 +785,7 @@ static int dot(DBusConnection *bus, char **args, unsigned n) { " red = Conflicts\n" " green = After\n"); - if (isatty(fileno(stdout))) + 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"); @@ -825,7 +814,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { assert(bus); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -852,7 +841,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { dbus_message_iter_recurse(&iter, &sub); - if (isatty(STDOUT_FILENO)) + if (on_tty()) printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE"); while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { @@ -888,7 +877,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { dbus_message_iter_next(&sub); } - if (isatty(STDOUT_FILENO)) + if (on_tty()) printf("\n%u jobs listed.\n", k); r = 0; @@ -2091,6 +2080,10 @@ static void print_status_info(UnitStatusInfo *i) { 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: { @@ -2098,7 +2091,7 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn dbus_message_iter_get_basic(iter, &s); - if (s[0]) { + if (!isempty(s)) { if (streq(name, "Id")) i->id = s; else if (streq(name, "LoadState")) @@ -2257,74 +2250,6 @@ static int print_property(const char *name, DBusMessageIter *iter) { switch (dbus_message_iter_get_arg_type(iter)) { - case DBUS_TYPE_STRING: { - const char *s; - dbus_message_iter_get_basic(iter, &s); - - if (arg_all || s[0]) - printf("%s=%s\n", name, s); - - return 0; - } - - case DBUS_TYPE_BOOLEAN: { - dbus_bool_t b; - dbus_message_iter_get_basic(iter, &b); - printf("%s=%s\n", name, yes_no(b)); - - return 0; - } - - case DBUS_TYPE_UINT64: { - uint64_t u; - dbus_message_iter_get_basic(iter, &u); - - /* Yes, heuristics! But we can change this check - * should it turn out to not be sufficient */ - - if (endswith(name, "Timestamp")) { - char timestamp[FORMAT_TIMESTAMP_MAX], *t; - - if ((t = format_timestamp(timestamp, sizeof(timestamp), u)) || arg_all) - printf("%s=%s\n", name, strempty(t)); - } else if (strstr(name, "USec")) { - char timespan[FORMAT_TIMESPAN_MAX]; - - printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u)); - } else - printf("%s=%llu\n", name, (unsigned long long) u); - - return 0; - } - - case DBUS_TYPE_UINT32: { - uint32_t u; - dbus_message_iter_get_basic(iter, &u); - - if (strstr(name, "UMask") || strstr(name, "Mode")) - printf("%s=%04o\n", name, u); - else - printf("%s=%u\n", name, (unsigned) u); - - return 0; - } - - case DBUS_TYPE_INT32: { - int32_t i; - dbus_message_iter_get_basic(iter, &i); - - printf("%s=%i\n", name, (int) i); - return 0; - } - - case DBUS_TYPE_DOUBLE: { - double d; - dbus_message_iter_get_basic(iter, &d); - - printf("%s=%g\n", name, d); - return 0; - } - case DBUS_TYPE_STRUCT: { DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); @@ -2356,55 +2281,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { case DBUS_TYPE_ARRAY: - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - bool space = false; - - dbus_message_iter_recurse(iter, &sub); - if (arg_all || - dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - printf("%s=", name); - - 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("%s%s", space ? " " : "", s); - - space = true; - dbus_message_iter_next(&sub); - } - - puts(""); - } - - return 0; - - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - if (arg_all || - dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - printf("%s=", name); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - uint8_t u; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE); - dbus_message_iter_get_basic(&sub, &u); - printf("%02x", u); - - dbus_message_iter_next(&sub); - } - - puts(""); - } - - return 0; - - } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) { + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) { DBusMessageIter sub, sub2; dbus_message_iter_recurse(iter, &sub); @@ -2509,6 +2386,9 @@ static int print_property(const char *name, DBusMessageIter *iter) { break; } + if (generic_print_property(name, iter, arg_all) > 0) + return 0; + if (arg_all) printf("%s=[unprintable]\n", name); @@ -2652,7 +2532,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) { show_properties = !streq(args[0], "status"); if (show_properties) - pager_open(); + pager_open_if_enabled(); if (show_properties && n <= 1) { /* If not argument is specified inspect the manager @@ -3048,7 +2928,7 @@ static int dump(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -3419,7 +3299,7 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -4363,10 +4243,9 @@ static int systemctl_help(void) { " pending\n" " --ignore-dependencies\n" " When queueing a new job, ignore all its dependencies\n" - " --kill-mode=MODE How to send signal\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" - " -H --host=[user@]host\n" + " -H --host=[USER@]HOST\n" " Show information for remote host\n" " -P --privileged Acquire privileges before execution\n" " -q --quiet Suppress output\n" @@ -4374,7 +4253,7 @@ static int systemctl_help(void) { " --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-pager Do not pipe output into a pager.\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" @@ -4547,7 +4426,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, 'f' }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, { "defaults", no_argument, NULL, ARG_DEFAULTS }, - { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, + { "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 }, @@ -5602,96 +5481,6 @@ static int runlevel_main(void) { return 0; } -static void pager_open(void) { - int fd[2]; - const char *pager; - pid_t parent_pid; - - if (pager_pid > 0) - return; - - if (!on_tty() || arg_no_pager) - return; - - if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER"))) - if (!*pager || streq(pager, "cat")) - return; - - /* Determine and cache number of columns before we spawn the - * pager so that we get the value from the actual tty */ - columns(); - - if (pipe(fd) < 0) { - log_error("Failed to create pager pipe: %m"); - return; - } - - parent_pid = getpid(); - - pager_pid = fork(); - if (pager_pid < 0) { - log_error("Failed to fork pager: %m"); - close_pipe(fd); - return; - } - - /* In the child start the pager */ - if (pager_pid == 0) { - - dup2(fd[0], STDIN_FILENO); - close_pipe(fd); - - setenv("LESS", "FRSX", 0); - - /* Make sure the pager goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - if (pager) { - execlp(pager, pager, NULL); - execl("/bin/sh", "sh", "-c", pager, NULL); - } else { - /* Debian's alternatives command for pagers is - * called 'pager'. Note that we do not call - * sensible-pagers here, since that is just a - * shell script that implements a logic that - * is similar to this one anyway, but is - * Debian-specific. */ - execlp("pager", "pager", NULL); - - execlp("less", "less", NULL); - execlp("more", "more", NULL); - } - - log_error("Unable to execute pager: %m"); - _exit(EXIT_FAILURE); - } - - /* Return in the parent */ - if (dup2(fd[1], STDOUT_FILENO) < 0) - log_error("Failed to duplicate pager pipe: %m"); - - close_pipe(fd); -} - -static void pager_close(void) { - siginfo_t dummy; - - if (pager_pid <= 0) - return; - - /* Inform pager that we are done */ - fclose(stdout); - kill(pager_pid, SIGCONT); - wait_for_terminate(pager_pid, &dummy); - pager_pid = 0; -} - static void agent_close(void) { siginfo_t dummy; diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c index b620aa6ee3..02b959ea59 100644 --- a/src/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent.c @@ -376,15 +376,17 @@ static int parse_password(const char *filename, char **wall) { release_terminal(); } - packet_length = 1+strlen(password)+1; - if (!(packet = new(char, packet_length))) - r = -ENOMEM; - else { - packet[0] = '+'; - strcpy(packet+1, password); - } + 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); + free(password); + } } if (r == -ETIME || r == -ENOENT) { diff --git a/src/unit.c b/src/unit.c index a2072621b0..d4142098d1 100644 --- a/src/unit.c +++ b/src/unit.c @@ -370,7 +370,7 @@ void unit_free(Unit *u) { u->meta.manager->n_in_gc_queue--; } - cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_serializing <= 0); + cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0); free(u->meta.description); free(u->meta.fragment_path); @@ -1137,7 +1137,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su * behaviour here. For example: if a mount point is remounted * this function will be called too! */ - if (u->meta.manager->n_deserializing <= 0) { + if (u->meta.manager->n_reloading <= 0) { dual_timestamp ts; dual_timestamp_get(&ts); @@ -1225,7 +1225,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (u->meta.manager->n_deserializing <= 0) { + if (u->meta.manager->n_reloading <= 0) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1258,7 +1258,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->meta.type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - u->meta.manager->n_deserializing <= 0) { + u->meta.manager->n_reloading <= 0) { /* Write audit record if we have just finished starting up */ manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, true); u->meta.in_audit = true; @@ -1275,7 +1275,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->meta.type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - u->meta.manager->n_deserializing <= 0) { + u->meta.manager->n_reloading <= 0) { /* Hmm, if there was no start record written * write it now, so that we always have a nice diff --git a/src/util.c b/src/util.c index f75df7b511..a0fbdc517e 100644 --- a/src/util.c +++ b/src/util.c @@ -53,6 +53,7 @@ #include <sys/capability.h> #include <sys/time.h> #include <linux/rtc.h> +#include <glob.h> #include "macro.h" #include "util.h" @@ -490,7 +491,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1)); char_array_0(fn); - if (!(f = fopen(fn, "r"))) + if (!(f = fopen(fn, "re"))) return -errno; if (!(fgets(line, sizeof(line), f))) { @@ -535,7 +536,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) { assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1)); char_array_0(fn); - if (!(f = fopen(fn, "r"))) + if (!(f = fopen(fn, "re"))) return -errno; if (!(fgets(line, sizeof(line), f))) { @@ -1010,7 +1011,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) return -ENOMEM; - f = fopen(p, "r"); + f = fopen(p, "re"); free(p); if (!f) @@ -2656,7 +2657,7 @@ int release_terminal(void) { int r = 0, fd; struct sigaction sa_old, sa_new; - if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY)) < 0) + if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0) return -errno; /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed @@ -2947,6 +2948,10 @@ int make_stdio(int fd) { if (r < 0 || s < 0 || t < 0) return -errno; + fd_cloexec(STDIN_FILENO, false); + fd_cloexec(STDOUT_FILENO, false); + fd_cloexec(STDERR_FILENO, false); + return 0; } @@ -3861,8 +3866,12 @@ char *normalize_env_assignment(const char *s) { } int wait_for_terminate(pid_t pid, siginfo_t *status) { + siginfo_t dummy; + assert(pid >= 1); - assert(status); + + if (!status) + status = &dummy; for (;;) { zero(*status); @@ -4272,7 +4281,7 @@ int detect_container(const char **id) { return 1; } - if ((f = fopen("/proc/self/cgroup", "r"))) { + if ((f = fopen("/proc/self/cgroup", "re"))) { for (;;) { char line[LINE_MAX], *p; @@ -5234,6 +5243,30 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h return 0; } +int glob_exists(const char *path) { + glob_t g; + int r, k; + + assert(path); + + zero(g); + errno = 0; + k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); + + if (k == GLOB_NOMATCH) + r = 0; + else if (k == GLOB_NOSPACE) + r = -ENOMEM; + else if (k == 0) + r = !strv_isempty(g.gl_pathv); + else + r = errno ? -errno : -EIO; + + globfree(&g); + + return r; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index 411efae933..b8bbd23e8c 100644 --- a/src/util.h +++ b/src/util.h @@ -447,6 +447,8 @@ int socket_from_display(const char *display, char **path); int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home); +int glob_exists(const char *path); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) |