From 2b45d88163b29f04bf784385f4a490b2cf206861 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 15:22:46 +0200 Subject: shared: fix minor memory leak in log display code --- src/shared/logs-show.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 38a55525c0..15c818ccf1 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1040,8 +1040,8 @@ static int show_journal(FILE *f, } int add_matches_for_unit(sd_journal *j, const char *unit) { + const char *m1, *m2, *m3, *m4; int r; - char *m1, *m2, *m3, *m4; assert(j); assert(unit); @@ -1073,7 +1073,9 @@ int add_matches_for_unit(sd_journal *j, const char *unit) { ); if (r == 0 && endswith(unit, ".slice")) { - const char *m5 = strjoina("_SYSTEMD_SLICE=", unit); + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); /* Show all messages belonging to a slice */ (void)( @@ -1123,7 +1125,9 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { ); if (r == 0 && endswith(unit, ".slice")) { - char *m5 = strappend("_SYSTEMD_SLICE=", unit); + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); /* Show all messages belonging to a slice */ (void)( -- cgit v1.2.3-54-g00ecf From 291d565a04263452c03beaf537773ade4f0b1617 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 15:28:28 +0200 Subject: core,systemctl: add bus API to retrieve processes of a unit This adds a new GetProcesses() bus call to the Unit object which returns an array consisting of all PIDs, their process names, as well as their full cgroup paths. This is then used by "systemctl status" to show the per-unit process tree. This has the benefit that the client-side no longer needs to access the cgroupfs directly to show the process tree of a unit. Instead, it now uses this new API, which means it also works if -H or -M are used correctly, as the information from the specific host is used, and not the one from the local system. Fixes: #2945 --- Makefile.am | 2 + src/basic/process-util.c | 12 + src/basic/process-util.h | 2 + src/core/busname.c | 10 + src/core/dbus-manager.c | 25 ++ src/core/dbus-unit.c | 143 +++++++++++ src/core/dbus-unit.h | 1 + src/core/mount.c | 10 + src/core/org.freedesktop.systemd1.conf | 4 + src/core/service.c | 19 ++ src/core/socket.c | 10 + src/core/swap.c | 10 + src/core/unit.c | 18 ++ src/core/unit.h | 9 + src/shared/bus-unit-util.c | 446 +++++++++++++++++++++++++++++++++ src/shared/bus-unit-util.h | 27 ++ src/shared/cgroup-show.c | 70 ++++-- src/systemctl/systemctl.c | 35 +-- 18 files changed, 819 insertions(+), 34 deletions(-) create mode 100644 src/shared/bus-unit-util.c create mode 100644 src/shared/bus-unit-util.h (limited to 'src') diff --git a/Makefile.am b/Makefile.am index f2ef83b0c7..b0118fd9a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1037,6 +1037,8 @@ libshared_la_SOURCES = \ src/shared/machine-pool.h \ src/shared/resolve-util.c \ src/shared/resolve-util.h \ + src/shared/bus-unit-util.c \ + src/shared/bus-unit-util.h \ src/shared/tests.h \ src/shared/tests.c diff --git a/src/basic/process-util.c b/src/basic/process-util.c index ae3f6109ad..f2cea01979 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -731,6 +731,18 @@ void valgrind_summary_hack(void) { #endif } +int pid_compare_func(const void *a, const void *b) { + const pid_t *p = a, *q = b; + + /* Suitable for usage in qsort() */ + + if (*p < *q) + return -1; + if (*p > *q) + return 1; + return 0; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index f5d193e762..ffd4bcb0ff 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -101,3 +101,5 @@ int sched_policy_from_string(const char *s); #define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) void valgrind_summary_hack(void); + +int pid_compare_func(const void *a, const void *b); diff --git a/src/core/busname.c b/src/core/busname.c index bbe61af4f0..f4f433340c 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -999,6 +999,14 @@ static bool busname_supported(void) { return supported; } +static int busname_control_pid(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + return n->control_pid; +} + static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_SUCCESS] = "success", [BUSNAME_FAILURE_RESOURCES] = "resources", @@ -1052,6 +1060,8 @@ const UnitVTable busname_vtable = { .supported = busname_supported, + .control_pid = busname_control_pid, + .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 0c86791fe3..d2eb388f7c 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -642,6 +642,30 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s return bus_unit_method_set_properties(message, u, error); } +static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + + r = bus_unit_check_load_state(u, error); + if (r < 0) + return r; + + return bus_unit_method_get_processes(message, u, error); +} + static int transient_unit_from_message( Manager *m, sd_bus_message *message, @@ -2042,6 +2066,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index c507265070..ed207f15b9 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -24,8 +24,10 @@ #include "cgroup-util.h" #include "dbus-unit.h" #include "dbus.h" +#include "fd-util.h" #include "locale-util.h" #include "log.h" +#include "process-util.h" #include "selinux-access.h" #include "signal-util.h" #include "special.h" @@ -841,6 +843,146 @@ static int property_get_cgroup( return sd_bus_message_append(reply, "s", t); } +static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) { + _cleanup_free_ char *buf = NULL, *cmdline = NULL; + int r; + + assert(reply); + assert(pid > 0); + + r = set_put(pids, PID_TO_PTR(pid)); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) + return r; + + if (!p) { + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf); + if (r == -ESRCH) + return 0; + if (r < 0) + return r; + + p = buf; + } + + (void) get_process_cmdline(pid, 0, true, &cmdline); + + return sd_bus_message_append(reply, + "(sus)", + p, + (uint32_t) pid, + cmdline); +} + +static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(reply); + assert(p); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); + if (r == ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + pid_t pid; + + r = cg_read_pid(f, &pid); + if (r < 0) + return r; + if (r == 0) + break; + + if (is_kernel_thread(pid) > 0) + continue; + + r = append_process(reply, p, pid, pids); + if (r < 0) + return r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *g = NULL, *j = NULL; + + r = cg_read_subgroup(d, &g); + if (r < 0) + return r; + if (r == 0) + break; + + j = strjoin(p, "/", g, NULL); + if (!j) + return -ENOMEM; + + r = append_cgroup(reply, j, pids); + if (r < 0) + return r; + } + + return 0; +} + +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(set_freep) Set *pids = NULL; + _cleanup_free_ char *p = NULL; + Unit *u = userdata; + pid_t pid; + int r; + + assert(message); + + pids = set_new(NULL); + if (!pids) + return -ENOMEM; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(sus)"); + if (r < 0) + return r; + + if (u->cgroup_path) { + r = append_cgroup(reply, u->cgroup_path, pids); + if (r < 0) + return r; + } + + /* The main and control pids might live outside of the cgroup, hence fetch them separately */ + pid = unit_main_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + pid = unit_control_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), @@ -848,6 +990,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), + SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 07948b9cd0..4db88dbebc 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -36,5 +36,6 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error); int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_check_load_state(Unit *u, sd_bus_error *error); diff --git a/src/core/mount.c b/src/core/mount.c index 632c5c824c..188fb0aa40 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1790,6 +1790,14 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); } +static int mount_control_pid(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->control_pid; +} + static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { [MOUNT_EXEC_MOUNT] = "ExecMount", [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", @@ -1851,6 +1859,8 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, + .control_pid = mount_control_pid, + .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index f78eedbd6e..b732501364 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -76,6 +76,10 @@ send_interface="org.freedesktop.systemd1.Manager" send_member="GetUnitFileState"/> + + diff --git a/src/core/service.c b/src/core/service.c index 58084e2f82..b46dd8bcdd 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3195,6 +3195,22 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); } +static int service_main_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->main_pid; +} + +static int service_control_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->control_pid; +} + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { [SERVICE_RESTART_NO] = "no", [SERVICE_RESTART_ON_SUCCESS] = "on-success", @@ -3303,6 +3319,9 @@ const UnitVTable service_vtable = { .notify_cgroup_empty = service_notify_cgroup_empty_event, .notify_message = service_notify_message, + .main_pid = service_main_pid, + .control_pid = service_control_pid, + .bus_name_owner_change = service_bus_name_owner_change, .bus_vtable = bus_service_vtable, diff --git a/src/core/socket.c b/src/core/socket.c index 65da0e3c5e..a9fff9c259 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2781,6 +2781,14 @@ char *socket_fdname(Socket *s) { return UNIT(s)->id; } +static int socket_control_pid(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + return s->control_pid; +} + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", [SOCKET_EXEC_START_CHOWN] = "StartChown", @@ -2846,6 +2854,8 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, + .control_pid = socket_control_pid, + .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/swap.c b/src/core/swap.c index c6502eb821..d8802470d2 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1426,6 +1426,14 @@ static bool swap_supported(void) { return supported; } +static int swap_control_pid(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + return s->control_pid; +} + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { [SWAP_EXEC_ACTIVATE] = "ExecActivate", [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", @@ -1487,6 +1495,8 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, + .control_pid = swap_control_pid, + .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/unit.c b/src/core/unit.c index 1f57293a0b..cb79c7c6b1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -3801,3 +3801,21 @@ bool unit_is_pristine(Unit *u) { u->job || u->merged_into); } + +pid_t unit_control_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->control_pid) + return UNIT_VTABLE(u)->control_pid(u); + + return 0; +} + +pid_t unit_main_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->main_pid) + return UNIT_VTABLE(u)->main_pid(u); + + return 0; +} diff --git a/src/core/unit.h b/src/core/unit.h index cfdac852a5..5909652976 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -390,6 +390,12 @@ struct UnitVTable { /* Returns the next timeout of a unit */ int (*get_timeout)(Unit *u, usec_t *timeout); + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*main_pid)(Unit *u); + + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*control_pid)(Unit *u); + /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, * everything that is loaded here should still stay in @@ -601,6 +607,9 @@ bool unit_type_supported(UnitType t); bool unit_is_pristine(Unit *u); +pid_t unit_control_pid(Unit *u); +pid_t unit_main_pid(Unit *u); + static inline bool unit_supported(Unit *u) { return unit_type_supported(u->type); } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c new file mode 100644 index 0000000000..25ecbd3df3 --- /dev/null +++ b/src/shared/bus-unit-util.c @@ -0,0 +1,446 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 . +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-unit-util.h" +#include "bus-util.h" +#include "cgroup-util.h" +#include "env-util.h" +#include "escape.h" +#include "hashmap.h" +#include "list.h" +#include "locale-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "string-util.h" +#include "syslog-util.h" +#include "terminal-util.h" +#include "utf8.h" +#include "util.h" + +struct CGroupInfo { + char *cgroup_path; + bool is_const; /* If false, cgroup_path should be free()'d */ + + Hashmap *pids; /* PID → process name */ + bool done; + + struct CGroupInfo *parent; + LIST_FIELDS(struct CGroupInfo, siblings); + LIST_HEAD(struct CGroupInfo, children); + size_t n_children; +}; + +static bool IS_ROOT(const char *p) { + return isempty(p) || streq(p, "/"); +} + +static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { + struct CGroupInfo *parent = NULL, *cg; + int r; + + assert(cgroups); + assert(ret); + + if (IS_ROOT(path)) + path = "/"; + + cg = hashmap_get(cgroups, path); + if (cg) { + *ret = cg; + return 0; + } + + if (!IS_ROOT(path)) { + const char *e, *pp; + + e = strrchr(path, '/'); + if (!e) + return -EINVAL; + + pp = strndupa(path, e - path); + if (!pp) + return -ENOMEM; + + r = add_cgroup(cgroups, pp, false, &parent); + if (r < 0) + return r; + } + + cg = new0(struct CGroupInfo, 1); + if (!cg) + return -ENOMEM; + + if (is_const) + cg->cgroup_path = (char*) path; + else { + cg->cgroup_path = strdup(path); + if (!cg->cgroup_path) { + free(cg); + return -ENOMEM; + } + } + + cg->is_const = is_const; + cg->parent = parent; + + r = hashmap_put(cgroups, cg->cgroup_path, cg); + if (r < 0) { + if (!is_const) + free(cg->cgroup_path); + free(cg); + return r; + } + + if (parent) { + LIST_PREPEND(siblings, parent->children, cg); + parent->n_children++; + } + + *ret = cg; + return 1; +} + +static int add_process( + Hashmap *cgroups, + const char *path, + pid_t pid, + const char *name) { + + struct CGroupInfo *cg; + int r; + + assert(cgroups); + assert(name); + assert(pid > 0); + + r = add_cgroup(cgroups, path, true, &cg); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); + if (r < 0) + return r; + + return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); +} + +static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { + assert(cgroups); + assert(cg); + + while (cg->children) + remove_cgroup(cgroups, cg->children); + + hashmap_remove(cgroups, cg->cgroup_path); + + if (!cg->is_const) + free(cg->cgroup_path); + + hashmap_free(cg->pids); + + if (cg->parent) + LIST_REMOVE(siblings, cg->parent->children, cg); + + free(cg); +} + +static int cgroup_info_compare_func(const void *a, const void *b) { + const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b; + + assert(x); + assert(y); + + return strcmp(x->cgroup_path, y->cgroup_path); +} + +static int dump_processes( + Hashmap *cgroups, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + struct CGroupInfo *cg; + int r; + + assert(prefix); + + if (IS_ROOT(cgroup_path)) + cgroup_path = "/"; + + cg = hashmap_get(cgroups, cgroup_path); + if (!cg) + return 0; + + if (!hashmap_isempty(cg->pids)) { + const char *name; + size_t n = 0, i; + pid_t *pids; + void *pidp; + Iterator j; + int width; + + /* Order processes by their PID */ + pids = newa(pid_t, hashmap_size(cg->pids)); + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) + pids[n++] = PTR_TO_PID(pidp); + + assert(n == hashmap_size(cg->pids)); + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *e = NULL; + const char *special; + bool more; + + name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); + assert(name); + + if (n_columns != 0) { + unsigned k; + + k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, k, 100); + if (e) + name = e; + } + + more = i+1 < n || cg->children; + special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + + fprintf(stdout, "%s%s%*"PID_PRI" %s\n", + prefix, + special, + width, pids[i], + name); + } + } + + if (cg->children) { + struct CGroupInfo **children, *child; + size_t n = 0, i; + + /* Order subcgroups by their name */ + children = newa(struct CGroupInfo*, cg->n_children); + LIST_FOREACH(siblings, child, cg->children) + children[n++] = child; + assert(n == cg->n_children); + qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func); + + n_columns = MAX(LESS_BY(n_columns, 2U), 20U); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *pp = NULL; + const char *name, *special; + bool more; + + child = children[i]; + + name = strrchr(child->cgroup_path, '/'); + if (!name) + return -EINVAL; + name++; + + more = i+1 < n; + special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + + fputs(prefix, stdout); + fputs(special, stdout); + fputs(name, stdout); + fputc('\n', stdout); + + special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE); + + pp = strappend(prefix, special); + if (!pp) + return -ENOMEM; + + r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); + if (r < 0) + return r; + } + } + + cg->done = true; + return 0; +} + +static int dump_extra_processes( + Hashmap *cgroups, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + _cleanup_free_ pid_t *pids = NULL; + _cleanup_hashmap_free_ Hashmap *names = NULL; + struct CGroupInfo *cg; + size_t n_allocated = 0, n = 0, k; + Iterator i; + int width, r; + + /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as + * combined, sorted, linear list. */ + + HASHMAP_FOREACH(cg, cgroups, i) { + const char *name; + void *pidp; + Iterator j; + + if (cg->done) + continue; + + if (hashmap_isempty(cg->pids)) + continue; + + r = hashmap_ensure_allocated(&names, &trivial_hash_ops); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) + return -ENOMEM; + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { + pids[n++] = PTR_TO_PID(pidp); + + r = hashmap_put(names, pidp, (void*) name); + if (r < 0) + return r; + } + } + + if (n == 0) + return 0; + + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (k = 0; k < n; k++) { + _cleanup_free_ char *e = NULL; + const char *name; + + name = hashmap_get(names, PID_TO_PTR(pids[k])); + assert(name); + + if (n_columns != 0) { + unsigned z; + + z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, z, 100); + if (e) + name = e; + } + + fprintf(stdout, "%s%s %*" PID_PRI " %s\n", + prefix, + draw_special_char(DRAW_TRIANGULAR_BULLET), + width, pids[k], + name); + } + + return 0; +} + +int unit_show_processes( + sd_bus *bus, + const char *unit, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Hashmap *cgroups = NULL; + struct CGroupInfo *cg; + int r; + + assert(bus); + assert(unit); + + if (flags & OUTPUT_FULL_WIDTH) + n_columns = 0; + else if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitProcesses", + error, + &reply, + "s", + unit); + if (r < 0) + return r; + + cgroups = hashmap_new(&string_hash_ops); + if (!cgroups) + return -ENOMEM; + + r = sd_bus_message_enter_container(reply, 'a', "(sus)"); + if (r < 0) + goto finish; + + for (;;) { + const char *path = NULL, *name = NULL; + uint32_t pid; + + r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); + if (r < 0) + goto finish; + if (r == 0) + break; + + r = add_process(cgroups, path, pid, name); + if (r < 0) + goto finish; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto finish; + + r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); + if (r < 0) + goto finish; + + r = dump_extra_processes(cgroups, prefix, n_columns, flags); + +finish: + while ((cg = hashmap_first(cgroups))) + remove_cgroup(cgroups, cg); + + hashmap_free(cgroups); + + return r; +} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h new file mode 100644 index 0000000000..55486f82ee --- /dev/null +++ b/src/shared/bus-unit-util.h @@ -0,0 +1,27 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 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 . +***/ + +#include "sd-bus.h" + +#include "output-mode.h" +#include "install.h" + +int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index f3039b23f7..65a2c554d5 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -37,23 +37,13 @@ #include "string-util.h" #include "terminal-util.h" -static int compare(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - if (*p < *q) - return -1; - if (*p > *q) - return 1; - return 0; -} - static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) { unsigned i, j, pid_width; if (n_pids == 0) return; - qsort(pids, n_pids, sizeof(pid_t), compare); + qsort(pids, n_pids, sizeof(pid_t), pid_compare_func); /* Filter duplicates */ for (j = 0, i = 1; i < n_pids; i++) { @@ -86,8 +76,14 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un } } +static int show_cgroup_one_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + bool more, + bool kernel_threads, + OutputFlags flags) { -static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) { char *fn; _cleanup_fclose_ FILE *f = NULL; size_t n = 0, n_allocated = 0; @@ -125,7 +121,13 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne return 0; } -int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + bool kernel_threads, + OutputFlags flags) { + _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL; _cleanup_closedir_ DIR *d = NULL; char *gn = NULL; @@ -137,8 +139,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns if (n_columns <= 0) n_columns = columns(); - if (!prefix) - prefix = ""; + prefix = strempty(prefix); r = cg_mangle_path(path, &fn); if (r < 0) @@ -202,7 +203,13 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns return 0; } -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup(const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + bool kernel_threads, + + OutputFlags flags) { _cleanup_free_ char *p = NULL; int r; @@ -215,7 +222,15 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags); } -static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) { +static int show_extra_pids( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + const pid_t pids[], + unsigned n_pids, + OutputFlags flags) { + _cleanup_free_ pid_t *copy = NULL; unsigned i, j; int r; @@ -252,7 +267,16 @@ static int show_extra_pids(const char *controller, const char *path, const char return 0; } -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + bool kernel_threads, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + int r; assert(path); @@ -264,7 +288,15 @@ int show_cgroup_and_extra(const char *controller, const char *path, const char * return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); } -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra_by_spec( + const char *spec, + const char *prefix, + unsigned n_columns, + bool kernel_threads, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + _cleanup_free_ char *controller = NULL, *path = NULL; int r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 04205411dd..c74fc11ca6 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -39,6 +39,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-message.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -3434,6 +3435,7 @@ typedef struct UnitStatusInfo { } UnitStatusInfo; static void print_status_info( + sd_bus *bus, UnitStatusInfo *i, bool *ellipsized) { @@ -3444,6 +3446,7 @@ static void print_status_info( char since2[FORMAT_TIMESTAMP_MAX], *s2; const char *path; char **t, **t2; + int r; assert(i); @@ -3716,25 +3719,26 @@ static void print_status_info( printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC)); } - if (i->control_group && - (i->main_pid > 0 || i->control_pid > 0 || - (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { + if (i->control_group) + printf(" CGroup: %s\n", i->control_group); + + { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + static const char prefix[] = " "; unsigned c; - printf(" CGroup: %s\n", i->control_group); + c = columns(); + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; + else + c = 0; - if (IN_SET(arg_transport, - BUS_TRANSPORT_LOCAL, - BUS_TRANSPORT_MACHINE)) { + r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error); + if (r == -EBADR) { unsigned k = 0; pid_t extra[2]; - static const char prefix[] = " "; - c = columns(); - if (c > sizeof(prefix) - 1) - c -= sizeof(prefix) - 1; - else - c = 0; + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ if (i->main_pid > 0) extra[k++] = i->main_pid; @@ -3743,7 +3747,8 @@ static void print_status_info( extra[k++] = i->control_pid; show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags()); - } + } else if (r < 0) + log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r)); } if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) @@ -4504,7 +4509,7 @@ static int show_one( if (streq(verb, "help")) show_unit_help(&info); else - print_status_info(&info, ellipsized); + print_status_info(bus, &info, ellipsized); } strv_free(info.documentation); -- cgit v1.2.3-54-g00ecf From fa7099927c87b91804c6cc5ec3a3d8f8b6ba9ada Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 15:32:24 +0200 Subject: networkd: consider various IPv6 features as disabled if IPv6 is not available in the kernel --- src/network/networkd-link.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 5cdf123652..5101d553d5 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -42,6 +42,9 @@ static bool link_dhcp6_enabled(Link *link) { assert(link); + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -90,6 +93,9 @@ static bool link_ipv4ll_enabled(Link *link) { static bool link_ipv6ll_enabled(Link *link) { assert(link); + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -192,6 +198,9 @@ static bool link_proxy_arp_enabled(Link *link) { static bool link_ipv6_accept_ra_enabled(Link *link) { assert(link); + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -214,6 +223,7 @@ static bool link_ipv6_accept_ra_enabled(Link *link) { } static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { + assert(link); if (!socket_ipv6_is_supported()) return _IPV6_PRIVACY_EXTENSIONS_INVALID; -- cgit v1.2.3-54-g00ecf From a0e270198a9d58b58dff44a6504843f19458facd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 15:51:33 +0200 Subject: loginctl,machinectl: also make use of new GetProcesses() bus call This ports over machinectl and loginctl to also use the new GetProcesses() bus call to show the process tree of a container or login session. This is similar to how systemctl already has been ported over in a previous commit. --- src/login/loginctl.c | 35 +++++++++++++++++++++++------------ src/machine/machinectl.c | 30 ++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 8b23135edd..7c6d2a1b0c 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -27,6 +27,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -227,18 +228,15 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; const char *cgroup; - int r; unsigned c; + int r; assert(bus); assert(unit); - if (arg_transport != BUS_TRANSPORT_LOCAL) - return 0; - path = unit_dbus_path_from_name(unit); if (!path) - return -ENOMEM; + return log_oom(); r = sd_bus_get_property( bus, @@ -246,27 +244,40 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit path, interface, "ControlGroup", - &error, &reply, "s"); + &error, + &reply, + "s"); if (r < 0) - return r; + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &cgroup); if (r < 0) - return r; + return bus_log_parse_error(r); if (isempty(cgroup)) return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) - return 0; - c = columns(); if (c > 18) c -= 18; else c = 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + return 0; } diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c370ed57ec..f47752fce2 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -33,6 +33,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -331,8 +332,8 @@ static int list_images(int argc, char *argv[], void *userdata) { } static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; const char *cgroup; int r; @@ -341,9 +342,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { assert(bus); assert(unit); - if (arg_transport == BUS_TRANSPORT_REMOTE) - return 0; - path = unit_dbus_path_from_name(unit); if (!path) return log_oom(); @@ -357,16 +355,14 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { &error, &reply, "s"); - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &cgroup); if (r < 0) return bus_log_parse_error(r); - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + if (isempty(cgroup)) return 0; c = columns(); @@ -375,7 +371,21 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { else c = 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + return 0; } -- cgit v1.2.3-54-g00ecf From 0ff308c8dea9a589ddbc437c097edc3fb26360d7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 16:06:58 +0200 Subject: shared: drop kernel_thread bool from cgroups show code Make this an output flag instead, so that our function prototypes can lose one parameter --- src/cgls/cgls.c | 11 ++++++----- src/login/loginctl.c | 2 +- src/machine/machinectl.c | 2 +- src/shared/cgroup-show.c | 36 +++++++++++++++++++----------------- src/shared/cgroup-show.h | 8 ++++---- src/shared/output-mode.h | 4 ++++ src/systemctl/systemctl.c | 4 ++-- 7 files changed, 37 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index d6fb10cac5..dcb5912b83 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -191,7 +191,8 @@ int main(int argc, char *argv[]) { output_flags = arg_all * OUTPUT_SHOW_ALL | - (arg_full > 0) * OUTPUT_FULL_WIDTH; + (arg_full > 0) * OUTPUT_FULL_WIDTH | + arg_kernel_threads * OUTPUT_KERNEL_THREADS; if (optind < argc) { _cleanup_free_ char *root = NULL; @@ -209,7 +210,7 @@ int main(int argc, char *argv[]) { printf("Directory %s:\n", argv[i]); fflush(stdout); - q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + q = show_cgroup_by_path(argv[i], NULL, 0, output_flags); } else { _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; const char *controller, *path; @@ -235,7 +236,7 @@ int main(int argc, char *argv[]) { show_cg_info(controller, path); - q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + q = show_cgroup(controller, path, NULL, 0, output_flags); } if (q < 0) @@ -258,7 +259,7 @@ int main(int argc, char *argv[]) { printf("Working directory %s:\n", cwd); fflush(stdout); - r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + r = show_cgroup_by_path(cwd, NULL, 0, output_flags); done = true; } } @@ -273,7 +274,7 @@ int main(int argc, char *argv[]) { show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); printf("-.slice\n"); - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); } } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 7c6d2a1b0c..1c75565636 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -274,7 +274,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); } else if (r < 0) return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index f47752fce2..7b1ede7116 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -382,7 +382,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); } else if (r < 0) return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 65a2c554d5..7539891bf2 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -37,7 +37,15 @@ #include "string-util.h" #include "terminal-util.h" -static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) { +static void show_pid_array( + pid_t pids[], + unsigned n_pids, + const char *prefix, + unsigned n_columns, + bool extra, + bool more, + OutputFlags flags) { + unsigned i, j, pid_width; if (n_pids == 0) @@ -81,7 +89,6 @@ static int show_cgroup_one_by_path( const char *prefix, unsigned n_columns, bool more, - bool kernel_threads, OutputFlags flags) { char *fn; @@ -103,7 +110,7 @@ static int show_cgroup_one_by_path( while ((r = cg_read_pid(f, &pid)) > 0) { - if (!kernel_threads && is_kernel_thread(pid) > 0) + if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0) continue; if (!GREEDY_REALLOC(pids, n_allocated, n + 1)) @@ -116,7 +123,7 @@ static int show_cgroup_one_by_path( if (r < 0) return r; - show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags); + show_pid_array(pids, n, prefix, n_columns, false, more, flags); return 0; } @@ -125,7 +132,6 @@ int show_cgroup_by_path( const char *path, const char *prefix, unsigned n_columns, - bool kernel_threads, OutputFlags flags) { _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL; @@ -161,7 +167,7 @@ int show_cgroup_by_path( continue; if (!shown_pids) { - show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags); + show_cgroup_one_by_path(path, prefix, n_columns, true, flags); shown_pids = true; } @@ -174,7 +180,7 @@ int show_cgroup_by_path( return -ENOMEM; } - show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags); + show_cgroup_by_path(last, p1, n_columns-2, flags); free(last); } @@ -186,7 +192,7 @@ int show_cgroup_by_path( return r; if (!shown_pids) - show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags); + show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags); if (last) { printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last))); @@ -197,7 +203,7 @@ int show_cgroup_by_path( return -ENOMEM; } - show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags); + show_cgroup_by_path(last, p2, n_columns-2, flags); } return 0; @@ -207,8 +213,6 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, - bool kernel_threads, - OutputFlags flags) { _cleanup_free_ char *p = NULL; int r; @@ -219,7 +223,7 @@ int show_cgroup(const char *controller, if (r < 0) return r; - return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags); + return show_cgroup_by_path(p, prefix, n_columns, flags); } static int show_extra_pids( @@ -262,7 +266,7 @@ static int show_extra_pids( copy[j++] = pids[i]; } - show_pid_array(copy, j, prefix, n_columns, true, false, false, flags); + show_pid_array(copy, j, prefix, n_columns, true, false, flags); return 0; } @@ -272,7 +276,6 @@ int show_cgroup_and_extra( const char *path, const char *prefix, unsigned n_columns, - bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { @@ -281,7 +284,7 @@ int show_cgroup_and_extra( assert(path); - r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags); + r = show_cgroup(controller, path, prefix, n_columns, flags); if (r < 0) return r; @@ -292,7 +295,6 @@ int show_cgroup_and_extra_by_spec( const char *spec, const char *prefix, unsigned n_columns, - bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { @@ -306,5 +308,5 @@ int show_cgroup_and_extra_by_spec( if (r < 0) return r; - return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags); + return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); } diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 3ab7dfb33c..5c1d6e6d98 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -25,8 +25,8 @@ #include "logs-show.h" #include "output-mode.h" -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags); -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index c5470e7c1b..e2b26e04d3 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -34,6 +34,9 @@ typedef enum OutputMode { _OUTPUT_MODE_INVALID = -1 } OutputMode; +/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the + * logs output, others only to the process tree output. */ + typedef enum OutputFlags { OUTPUT_SHOW_ALL = 1 << 0, OUTPUT_FOLLOW = 1 << 1, @@ -43,4 +46,5 @@ typedef enum OutputFlags { OUTPUT_CATALOG = 1 << 5, OUTPUT_BEGIN_NEWLINE = 1 << 6, OUTPUT_UTC = 1 << 7, + OUTPUT_KERNEL_THREADS = 1 << 8, } OutputFlags; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index c74fc11ca6..9c59fcf610 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3746,7 +3746,7 @@ static void print_status_info( if (i->control_pid > 0) extra[k++] = i->control_pid; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags()); + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags()); } else if (r < 0) log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r)); } @@ -4663,7 +4663,7 @@ static int show_system_status(sd_bus *bus) { else c = 0; - show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, get_output_flags()); + show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags()); } return 0; -- cgit v1.2.3-54-g00ecf From 20b1644140c02b436d5efb203f40c9b80a4579a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 17:43:13 +0200 Subject: shared: move unit-specific code from bus-util.h to bus-unit-util.h Previously we'd have generally useful sd-bus utilities in bust-util.h, intermixed with code that is specifically for writing clients for PID 1, wrapping job and unit handling. Let's split the latter out and move it into bus-unit-util.c, to make the sources a bit short and easier to grok. --- src/analyze/analyze.c | 1 + src/nspawn/nspawn-register.c | 1 + src/run/run.c | 1 + src/shared/bus-unit-util.c | 841 ++++++++++++++++++++++++++++++++++++++++++ src/shared/bus-unit-util.h | 30 ++ src/shared/bus-util.c | 859 ------------------------------------------- src/shared/bus-util.h | 33 -- 7 files changed, 874 insertions(+), 892 deletions(-) (limited to 'src') diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 5e03c0c5e0..a790ccd33e 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "analyze-verify.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "glob-util.h" #include "hashmap.h" diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 760861089d..20103c5e88 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -20,6 +20,7 @@ #include "sd-bus.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "nspawn-register.h" #include "stat-util.h" diff --git a/src/run/run.c b/src/run/run.c index 1993a424ca..4a0143d4e6 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "calendarspec.h" #include "env-util.h" diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 25ecbd3df3..da479aec8d 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -38,6 +38,847 @@ #include "utf8.h" #include "util.h" +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { + assert(message); + assert(u); + + u->machine = NULL; + + return sd_bus_message_read( + message, + "(ssssssouso)", + &u->id, + &u->description, + &u->load_state, + &u->active_state, + &u->sub_state, + &u->following, + &u->unit_path, + &u->job_id, + &u->job_type, + &u->job_path); +} + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { + const char *eq, *field; + int r, rl; + + assert(m); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + field = strndupa(assignment, eq - assignment); + eq++; + + if (streq(field, "CPUQuota")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); + else if (endswith(eq, "%")) { + double percent; + + if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { + log_error("CPU quota '%s' invalid.", eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); + } else { + log_error("CPU quota needs to be in percent."); + return -EINVAL; + } + + goto finish; + + } else if (streq(field, "EnvironmentFile")) { + + r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); + goto finish; + + } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { + char *n; + usec_t t; + size_t l; + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + + l = strlen(field); + n = newa(char, l + 2); + if (!n) + return log_oom(); + + /* Change suffix Sec → USec */ + strcpy(mempcpy(n, field, l - 3), "USec"); + r = sd_bus_message_append(m, "sv", n, "t", t); + goto finish; + } + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + rl = rlimit_from_string(field); + if (rl >= 0) { + const char *sn; + struct rlimit l; + + r = rlimit_parse(rl, eq, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq); + + r = sd_bus_message_append(m, "v", "t", l.rlim_max); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); + + } else if (STR_IN_SET(field, + "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { + + r = parse_boolean(eq); + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); + + r = sd_bus_message_append(m, "v", "b", r); + + } else if (streq(field, "MemoryLimit")) { + uint64_t bytes; + + if (isempty(eq) || streq(eq, "infinity")) + bytes = (uint64_t) -1; + else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", bytes); + + } else if (streq(field, "TasksMax")) { + uint64_t n; + + if (isempty(eq) || streq(eq, "infinity")) + n = (uint64_t) -1; + else { + r = safe_atou64(eq, &n); + if (r < 0) { + log_error("Failed to parse maximum tasks specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", n); + + } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, + "User", "Group", "DevicePolicy", "KillMode", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "StandardInput", "StandardOutput", "StandardError", + "Description", "Slice", "Type", "WorkingDirectory", + "RootDirectory", "SyslogIdentifier", "ProtectSystem", + "ProtectHome")) + r = sd_bus_message_append(m, "v", "s", eq); + + else if (streq(field, "SyslogLevel")) { + int level; + + level = log_level_from_string(eq); + if (level < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", level); + + } else if (streq(field, "SyslogFacility")) { + int facility; + + facility = log_facility_unshifted_from_string(eq); + if (facility < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", facility); + + } else if (streq(field, "DeviceAllow")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(ss)", 0); + else { + const char *path, *rwm, *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + rwm = e+1; + } else { + path = eq; + rwm = ""; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); + } + + } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *bandwidth, *e; + uint64_t bytes; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + bandwidth = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = parse_size(bandwidth, 1000, &bytes); + if (r < 0) { + log_error("Failed to parse byte value %s.", bandwidth); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); + } + + } else if (streq(field, "BlockIODeviceWeight")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *weight, *e; + uint64_t u; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + weight = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = safe_atou64(weight, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, weight); + return -EINVAL; + } + r = sd_bus_message_append(m, "v", "a(st)", path, u); + } + + } else if (streq(field, "Nice")) { + int32_t i; + + r = safe_atoi32(eq, &i); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", i); + + } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) { + log_error("Failed to parse Environment value %s", eq); + return -EINVAL; + } + if (r == 0) + break; + + if (streq(field, "Environment")) { + if (!env_assignment_is_valid(word)) { + log_error("Invalid environment assignment: %s", word); + return -EINVAL; + } + } else { /* PassEnvironment */ + if (!env_name_is_valid(word)) { + log_error("Invalid environment variable name: %s", word); + return -EINVAL; + } + } + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "KillSignal")) { + int sig; + + sig = signal_from_string_try_harder(eq); + if (sig < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", sig); + + } else if (streq(field, "TimerSlackNSec")) { + nsec_t n; + + r = parse_nsec(eq, &n); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", n); + } else if (streq(field, "OOMScoreAdjust")) { + int oa; + + r = safe_atoi(eq, &oa); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + if (!oom_score_adjust_is_valid(oa)) { + log_error("OOM score adjust value out of range"); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", oa); + } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + int offset; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + if (r == 0) + break; + + if (!utf8_is_valid(word)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + offset = word[0] == '-'; + if (!path_is_absolute(word + offset)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + path_kill_slashes(word + offset); + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "RuntimeDirectory")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s", field, eq); + + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else { + log_error("Unknown assignment %s.", assignment); + return -EINVAL; + } + +finish: + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 0; +} + +typedef struct BusWaitForJobs { + sd_bus *bus; + Set *jobs; + + char *name; + char *result; + + sd_bus_slot *slot_job_removed; + sd_bus_slot *slot_disconnected; +} BusWaitForJobs; + +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { + assert(m); + + log_error("Warning! D-Bus connection terminated."); + sd_bus_close(sd_bus_message_get_bus(m)); + + return 0; +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char *path, *unit, *result; + BusWaitForJobs *d = userdata; + uint32_t id; + char *found; + int r; + + assert(m); + assert(d); + + r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + found = set_remove(d->jobs, (char*) path); + if (!found) + return 0; + + free(found); + + if (!isempty(result)) + d->result = strdup(result); + + if (!isempty(unit)) + d->name = strdup(unit); + + return 0; +} + +void bus_wait_for_jobs_free(BusWaitForJobs *d) { + if (!d) + return; + + set_free_free(d->jobs); + + sd_bus_slot_unref(d->slot_disconnected); + sd_bus_slot_unref(d->slot_job_removed); + + sd_bus_unref(d->bus); + + free(d->name); + free(d->result); + + free(d); +} + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; + int r; + + assert(bus); + assert(ret); + + d = new0(BusWaitForJobs, 1); + if (!d) + return -ENOMEM; + + d->bus = sd_bus_ref(bus); + + /* When we are a bus client we match by sender. Direct + * connections OTOH have no initialized sender field, and + * hence we ignore the sender then */ + r = sd_bus_add_match( + bus, + &d->slot_job_removed, + bus->bus_client ? + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'" : + "type='signal'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + match_job_removed, d); + if (r < 0) + return r; + + r = sd_bus_add_match( + bus, + &d->slot_disconnected, + "type='signal'," + "sender='org.freedesktop.DBus.Local'," + "interface='org.freedesktop.DBus.Local'," + "member='Disconnected'", + match_disconnected, d); + if (r < 0) + return r; + + *ret = d; + d = NULL; + + return 0; +} + +static int bus_process_wait(sd_bus *bus) { + int r; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static const struct { + const char *result, *explanation; +} explanations [] = { + { "resources", "a configured resource limit was exceeded" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { + _cleanup_free_ char *service_shell_quoted = NULL; + const char *systemctl = "systemctl", *journalctl = "journalctl"; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service); + + if (extra_args && extra_args[1]) { + _cleanup_free_ char *t; + + t = strv_join((char**) extra_args, " "); + systemctl = strjoina("systemctl ", t ? : ""); + journalctl = strjoina("journalctl ", t ? : ""); + } + + if (!isempty(result)) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + explanations[i].explanation, + systemctl, + service_shell_quoted ?: "", + journalctl); + goto finish; + } + } + + log_error("Job for %s failed.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + systemctl, + service_shell_quoted ?: "", + journalctl); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"%1$s reset-failed %2$s\"\n" + "followed by \"%1$s start %2$s\" again.", + systemctl, + service_shell_quoted ?: ""); +} + +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d->result); + + if (!quiet) { + if (streq(d->result, "canceled")) + log_error("Job for %s canceled.", strna(d->name)); + else if (streq(d->result, "timeout")) + log_error("Job for %s timed out.", strna(d->name)); + else if (streq(d->result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); + else if (streq(d->result, "invalid")) + log_error("%s is not active, cannot reload.", strna(d->name)); + else if (streq(d->result, "assert")) + log_error("Assertion failed on job for %s.", strna(d->name)); + else if (streq(d->result, "unsupported")) + log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { + if (d->name) { + int q; + _cleanup_free_ char *result = NULL; + + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); + + log_job_error_with_service_result(d->name, result, extra_args); + } else + log_error("Job failed. See \"journalctl -xe\" for details."); + } + } + + if (streq(d->result, "canceled")) + r = -ECANCELED; + else if (streq(d->result, "timeout")) + r = -ETIME; + else if (streq(d->result, "dependency")) + r = -EIO; + else if (streq(d->result, "invalid")) + r = -ENOEXEC; + else if (streq(d->result, "assert")) + r = -EPROTO; + else if (streq(d->result, "unsupported")) + r = -EOPNOTSUPP; + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) + r = -EIO; + + return r; +} + +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d); + + while (!set_isempty(d->jobs)) { + int q; + + q = bus_process_wait(d->bus); + if (q < 0) + return log_error_errno(q, "Failed to wait for response: %m"); + + if (d->result) { + q = check_wait_response(d, quiet, extra_args); + /* Return the first error as it is most likely to be + * meaningful. */ + if (q < 0 && r == 0) + r = q; + + log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); + } + + d->name = mfree(d->name); + d->result = mfree(d->result); + } + + return r; +} + +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { + int r; + + assert(d); + + r = set_ensure_allocated(&d->jobs, &string_hash_ops); + if (r < 0) + return r; + + return set_put_strdup(d->jobs, path); +} + +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { + int r; + + r = bus_wait_for_jobs_add(d, path); + if (r < 0) + return log_oom(); + + return bus_wait_for_jobs(d, quiet, NULL); +} + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { + const char *type, *path, *source; + int r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { + /* We expect only "success" changes to be sent over the bus. + Hence, reject anything negative. */ + UnitFileChangeType ch = unit_file_change_type_from_string(type); + + if (ch < 0) { + log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); + continue; + } + + r = unit_file_changes_add(changes, n_changes, ch, path, source); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + unit_file_dump_changes(0, NULL, *changes, *n_changes, false); + return 0; +} + struct CGroupInfo { char *cgroup_path; bool is_const; /* If false, cgroup_path should be free()'d */ diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index 55486f82ee..c0c172f336 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -24,4 +24,34 @@ #include "output-mode.h" #include "install.h" +typedef struct UnitInfo { + const char *machine; + 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; +} UnitInfo; + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); + +typedef struct BusWaitForJobs BusWaitForJobs; + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); +void bus_wait_for_jobs_free(BusWaitForJobs *d); +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); + int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 6a1877d8aa..4efbf3710f 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -39,34 +39,16 @@ #include "bus-label.h" #include "bus-message.h" #include "bus-util.h" -#include "cgroup-util.h" #include "def.h" -#include "env-util.h" #include "escape.h" -#include "extract-word.h" #include "fd-util.h" -#include "hashmap.h" -#include "install.h" -#include "kdbus.h" -#include "log.h" -#include "macro.h" #include "missing.h" #include "parse-util.h" -#include "path-util.h" #include "proc-cmdline.h" -#include "process-util.h" #include "rlimit-util.h" -#include "set.h" -#include "signal-util.h" #include "stdio-util.h" -#include "string-util.h" #include "strv.h" -#include "syslog-util.h" -#include "time-util.h" -#include "unit-name.h" #include "user-util.h" -#include "utf8.h" -#include "util.h" static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; @@ -1383,847 +1365,6 @@ int bus_log_create_error(int r) { return log_error_errno(r, "Failed to create bus message: %m"); } -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { - assert(message); - assert(u); - - u->machine = NULL; - - return sd_bus_message_read( - message, - "(ssssssouso)", - &u->id, - &u->description, - &u->load_state, - &u->active_state, - &u->sub_state, - &u->following, - &u->unit_path, - &u->job_id, - &u->job_type, - &u->job_path); -} - -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { - const char *eq, *field; - int r, rl; - - assert(m); - assert(assignment); - - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - field = strndupa(assignment, eq - assignment); - eq++; - - if (streq(field, "CPUQuota")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); - else if (endswith(eq, "%")) { - double percent; - - if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { - log_error("CPU quota '%s' invalid.", eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); - } else { - log_error("CPU quota needs to be in percent."); - return -EINVAL; - } - - goto finish; - - } else if (streq(field, "EnvironmentFile")) { - - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); - goto finish; - - } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { - char *n; - usec_t t; - size_t l; - r = parse_sec(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - - l = strlen(field); - n = newa(char, l + 2); - if (!n) - return log_oom(); - - /* Change suffix Sec → USec */ - strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_append(m, "sv", n, "t", t); - goto finish; - } - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - rl = rlimit_from_string(field); - if (rl >= 0) { - const char *sn; - struct rlimit l; - - r = rlimit_parse(rl, eq, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", eq); - - r = sd_bus_message_append(m, "v", "t", l.rlim_max); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); - - } else if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { - - r = parse_boolean(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); - - r = sd_bus_message_append(m, "v", "b", r); - - } else if (streq(field, "MemoryLimit")) { - uint64_t bytes; - - if (isempty(eq) || streq(eq, "infinity")) - bytes = (uint64_t) -1; - else { - r = parse_size(eq, 1024, &bytes); - if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); - return -EINVAL; - } - } - - r = sd_bus_message_append(m, "v", "t", bytes); - - } else if (streq(field, "TasksMax")) { - uint64_t n; - - if (isempty(eq) || streq(eq, "infinity")) - n = (uint64_t) -1; - else { - r = safe_atou64(eq, &n); - if (r < 0) { - log_error("Failed to parse maximum tasks specification %s", assignment); - return -EINVAL; - } - } - - r = sd_bus_message_append(m, "v", "t", n); - - } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, - "User", "Group", "DevicePolicy", "KillMode", - "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "StandardInput", "StandardOutput", "StandardError", - "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome")) - r = sd_bus_message_append(m, "v", "s", eq); - - else if (streq(field, "SyslogLevel")) { - int level; - - level = log_level_from_string(eq); - if (level < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", level); - - } else if (streq(field, "SyslogFacility")) { - int facility; - - facility = log_facility_unshifted_from_string(eq); - if (facility < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", facility); - - } else if (streq(field, "DeviceAllow")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(ss)", 0); - else { - const char *path, *rwm, *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); - } - - } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *bandwidth, *e; - uint64_t bytes; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - bandwidth = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) { - log_error("Failed to parse byte value %s.", bandwidth); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); - } - - } else if (streq(field, "BlockIODeviceWeight")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *weight, *e; - uint64_t u; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - weight = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = safe_atou64(weight, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, weight); - return -EINVAL; - } - r = sd_bus_message_append(m, "v", "a(st)", path, u); - } - - } else if (streq(field, "Nice")) { - int32_t i; - - r = safe_atoi32(eq, &i); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", i); - - } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) { - log_error("Failed to parse Environment value %s", eq); - return -EINVAL; - } - if (r == 0) - break; - - if (streq(field, "Environment")) { - if (!env_assignment_is_valid(word)) { - log_error("Invalid environment assignment: %s", word); - return -EINVAL; - } - } else { /* PassEnvironment */ - if (!env_name_is_valid(word)) { - log_error("Invalid environment variable name: %s", word); - return -EINVAL; - } - } - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "KillSignal")) { - int sig; - - sig = signal_from_string_try_harder(eq); - if (sig < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", sig); - - } else if (streq(field, "TimerSlackNSec")) { - nsec_t n; - - r = parse_nsec(eq, &n); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", n); - } else if (streq(field, "OOMScoreAdjust")) { - int oa; - - r = safe_atoi(eq, &oa); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - if (!oom_score_adjust_is_valid(oa)) { - log_error("OOM score adjust value out of range"); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", oa); - } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - int offset; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - if (r == 0) - break; - - if (!utf8_is_valid(word)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - offset = word[0] == '-'; - if (!path_is_absolute(word + offset)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - path_kill_slashes(word + offset); - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "RuntimeDirectory")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s", field, eq); - - if (r == 0) - break; - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else { - log_error("Unknown assignment %s.", assignment); - return -EINVAL; - } - -finish: - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 0; -} - -typedef struct BusWaitForJobs { - sd_bus *bus; - Set *jobs; - - char *name; - char *result; - - sd_bus_slot *slot_job_removed; - sd_bus_slot *slot_disconnected; -} BusWaitForJobs; - -static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(m); - - log_error("Warning! D-Bus connection terminated."); - sd_bus_close(sd_bus_message_get_bus(m)); - - return 0; -} - -static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char *path, *unit, *result; - BusWaitForJobs *d = userdata; - uint32_t id; - char *found; - int r; - - assert(m); - assert(d); - - r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - found = set_remove(d->jobs, (char*) path); - if (!found) - return 0; - - free(found); - - if (!isempty(result)) - d->result = strdup(result); - - if (!isempty(unit)) - d->name = strdup(unit); - - return 0; -} - -void bus_wait_for_jobs_free(BusWaitForJobs *d) { - if (!d) - return; - - set_free_free(d->jobs); - - sd_bus_slot_unref(d->slot_disconnected); - sd_bus_slot_unref(d->slot_job_removed); - - sd_bus_unref(d->bus); - - free(d->name); - free(d->result); - - free(d); -} - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { - _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; - int r; - - assert(bus); - assert(ret); - - d = new0(BusWaitForJobs, 1); - if (!d) - return -ENOMEM; - - d->bus = sd_bus_ref(bus); - - /* When we are a bus client we match by sender. Direct - * connections OTOH have no initialized sender field, and - * hence we ignore the sender then */ - r = sd_bus_add_match( - bus, - &d->slot_job_removed, - bus->bus_client ? - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'" : - "type='signal'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, d); - if (r < 0) - return r; - - r = sd_bus_add_match( - bus, - &d->slot_disconnected, - "type='signal'," - "sender='org.freedesktop.DBus.Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - match_disconnected, d); - if (r < 0) - return r; - - *ret = d; - d = NULL; - - return 0; -} - -static int bus_process_wait(sd_bus *bus) { - int r; - - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (r > 0) - return 0; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } -} - -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { - _cleanup_free_ char *dbus_path = NULL; - - assert(d); - assert(d->name); - assert(result); - - dbus_path = unit_dbus_path_from_name(d->name); - if (!dbus_path) - return -ENOMEM; - - return sd_bus_get_property_string(d->bus, - "org.freedesktop.systemd1", - dbus_path, - "org.freedesktop.systemd1.Service", - "Result", - NULL, - result); -} - -static const struct { - const char *result, *explanation; -} explanations [] = { - { "resources", "a configured resource limit was exceeded" }, - { "timeout", "a timeout was exceeded" }, - { "exit-code", "the control process exited with error code" }, - { "signal", "a fatal signal was delivered to the control process" }, - { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, - { "watchdog", "the service failed to send watchdog ping" }, - { "start-limit", "start of the service was attempted too often" } -}; - -static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { - _cleanup_free_ char *service_shell_quoted = NULL; - const char *systemctl = "systemctl", *journalctl = "journalctl"; - - assert(service); - - service_shell_quoted = shell_maybe_quote(service); - - if (extra_args && extra_args[1]) { - _cleanup_free_ char *t; - - t = strv_join((char**) extra_args, " "); - systemctl = strjoina("systemctl ", t ? : ""); - journalctl = strjoina("journalctl ", t ? : ""); - } - - if (!isempty(result)) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(explanations); ++i) - if (streq(result, explanations[i].result)) - break; - - if (i < ELEMENTSOF(explanations)) { - log_error("Job for %s failed because %s.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - explanations[i].explanation, - systemctl, - service_shell_quoted ?: "", - journalctl); - goto finish; - } - } - - log_error("Job for %s failed.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - systemctl, - service_shell_quoted ?: "", - journalctl); - -finish: - /* For some results maybe additional explanation is required */ - if (streq_ptr(result, "start-limit")) - log_info("To force a start use \"%1$s reset-failed %2$s\"\n" - "followed by \"%1$s start %2$s\" again.", - systemctl, - service_shell_quoted ?: ""); -} - -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { - int r = 0; - - assert(d->result); - - if (!quiet) { - if (streq(d->result, "canceled")) - log_error("Job for %s canceled.", strna(d->name)); - else if (streq(d->result, "timeout")) - log_error("Job for %s timed out.", strna(d->name)); - else if (streq(d->result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); - else if (streq(d->result, "invalid")) - log_error("%s is not active, cannot reload.", strna(d->name)); - else if (streq(d->result, "assert")) - log_error("Assertion failed on job for %s.", strna(d->name)); - else if (streq(d->result, "unsupported")) - log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { - if (d->name) { - int q; - _cleanup_free_ char *result = NULL; - - q = bus_job_get_service_result(d, &result); - if (q < 0) - log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - - log_job_error_with_service_result(d->name, result, extra_args); - } else - log_error("Job failed. See \"journalctl -xe\" for details."); - } - } - - if (streq(d->result, "canceled")) - r = -ECANCELED; - else if (streq(d->result, "timeout")) - r = -ETIME; - else if (streq(d->result, "dependency")) - r = -EIO; - else if (streq(d->result, "invalid")) - r = -ENOEXEC; - else if (streq(d->result, "assert")) - r = -EPROTO; - else if (streq(d->result, "unsupported")) - r = -EOPNOTSUPP; - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) - r = -EIO; - - return r; -} - -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { - int r = 0; - - assert(d); - - while (!set_isempty(d->jobs)) { - int q; - - q = bus_process_wait(d->bus); - if (q < 0) - return log_error_errno(q, "Failed to wait for response: %m"); - - if (d->result) { - q = check_wait_response(d, quiet, extra_args); - /* Return the first error as it is most likely to be - * meaningful. */ - if (q < 0 && r == 0) - r = q; - - log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); - } - - d->name = mfree(d->name); - d->result = mfree(d->result); - } - - return r; -} - -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { - int r; - - assert(d); - - r = set_ensure_allocated(&d->jobs, &string_hash_ops); - if (r < 0) - return r; - - return set_put_strdup(d->jobs, path); -} - -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { - int r; - - r = bus_wait_for_jobs_add(d, path); - if (r < 0) - return log_oom(); - - return bus_wait_for_jobs(d, quiet, NULL); -} - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { - const char *type, *path, *source; - int r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { - /* We expect only "success" changes to be sent over the bus. - Hence, reject anything negative. */ - UnitFileChangeType ch = unit_file_change_type_from_string(type); - - if (ch < 0) { - log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); - continue; - } - - r = unit_file_changes_add(changes, n_changes, ch, path, source); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - unit_file_dump_changes(0, NULL, *changes, *n_changes, false); - return 0; -} - /** * bus_path_encode_unique() - encode unique object path * @b: bus connection or NULL diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 1a0841ce81..d792258ecd 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -24,15 +24,12 @@ #include #include -#include "sd-bus-vtable.h" #include "sd-bus.h" #include "sd-event.h" #include "hashmap.h" -#include "install.h" #include "macro.h" #include "string-util.h" -#include "time-util.h" typedef enum BusTransport { BUS_TRANSPORT_LOCAL, @@ -126,22 +123,6 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t)); int bus_log_parse_error(int r); int bus_log_create_error(int r); -typedef struct UnitInfo { - const char *machine; - 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; -} UnitInfo; - -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); - #define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ int function(sd_bus *bus, \ const char *path, \ @@ -173,20 +154,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); - -typedef struct BusWaitForJobs BusWaitForJobs; - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); -void bus_wait_for_jobs_free(BusWaitForJobs *d); -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); - -DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); - int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); -- cgit v1.2.3-54-g00ecf From f53d86c9295d9ac20981ca26beb6b83ad1f4296d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 17:49:31 +0200 Subject: machine-id-setup: explicitly fsync() the machine ID after writing As discussed here: https://github.com/systemd/systemd/issues/2619#issuecomment-184670042 Explicitly syncing /etc/machine-id after writing it, is probably a good idea, since it has a strong "commit" character and is generally a one-time thing. Fixes #2619. --- src/core/machine-id-setup.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 9de528b6cf..0145fe2894 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -101,14 +101,23 @@ static int read_machine_id(int fd, char id[34]) { return 0; } -static int write_machine_id(int fd, char id[34]) { +static int write_machine_id(int fd, const char id[34]) { + int r; + assert(fd >= 0); assert(id); if (lseek(fd, 0, SEEK_SET) < 0) return -errno; - return loop_write(fd, id, 33, false); + r = loop_write(fd, id, 33, false); + if (r < 0) + return r; + + if (fsync(fd) < 0) + return -errno; + + return 0; } static int generate_machine_id(char id[34], const char *root) { -- cgit v1.2.3-54-g00ecf From 3411372e35fd199d46910b79b8017e9e4ffc8da8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 18:12:57 +0200 Subject: tree-wide: don't assume CLOCK_BOOTIME is generally available Before we invoke now(CLOCK_BOOTTIME), let's make sure we actually have that clock, since now() will otherwise hit an assert. Specifically, let's refuse CLOCK_BOOTTIME early in sd-event if the kernel doesn't actually support it. This is a follow-up for #3037, and specifically: https://github.com/systemd/systemd/pull/3037#issuecomment-210199167 --- src/basic/time-util.c | 37 ++++++++++++++++++++++-------------- src/basic/time-util.h | 1 + src/core/timer.c | 2 +- src/libsystemd/sd-event/sd-event.c | 10 +++++++++- src/libsystemd/sd-event/test-event.c | 12 ++++++++---- 5 files changed, 42 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 7ca764abeb..edd9179cb8 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1080,22 +1080,31 @@ bool timezone_is_valid(const char *name) { return true; } -clockid_t clock_boottime_or_monotonic(void) { - static clockid_t clock = -1; - int fd; - - if (clock != -1) - return clock; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - clock = CLOCK_MONOTONIC; - else { - safe_close(fd); - clock = CLOCK_BOOTTIME; +bool clock_boottime_supported(void) { + static int supported = -1; + + /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ + + if (supported < 0) { + int fd; + + fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + supported = false; + else { + safe_close(fd); + supported = true; + } } - return clock; + return supported; +} + +clockid_t clock_boottime_or_monotonic(void) { + if (clock_boottime_supported()) + return CLOCK_BOOTTIME; + else + return CLOCK_MONOTONIC; } int get_timezone(char **tz) { diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 77e3cd08d4..a5e3f567ec 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -112,6 +112,7 @@ bool ntp_synced(void); int get_timezones(char ***l); bool timezone_is_valid(const char *name); +bool clock_boottime_supported(void); clockid_t clock_boottime_or_monotonic(void); #define xstrftime(buf, fmt, tm) \ diff --git a/src/core/timer.c b/src/core/timer.c index b286b714fa..f8f5f4b2e4 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -373,7 +373,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { * rather than the monotonic clock. */ ts_realtime = now(CLOCK_REALTIME); - ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC); + ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC); t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0; LIST_FOREACH(value, v, t->values) { diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 79b98c293c..7ba6527f63 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -1072,6 +1072,10 @@ _public_ int sd_event_add_time( assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && + !clock_boottime_supported()) + return -EOPNOTSUPP; + if (!callback) callback = time_exit_callback; @@ -2527,7 +2531,8 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { } dual_timestamp_get(&e->timestamp); - e->timestamp_boottime = now(clock_boottime_or_monotonic()); + if (clock_boottime_supported()) + e->timestamp_boottime = now(CLOCK_BOOTTIME); for (i = 0; i < m; i++) { @@ -2761,6 +2766,9 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP); + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) + return -EOPNOTSUPP; + if (!dual_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran * before and thus have no cached time. */ diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index fd31588b8f..289114490c 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -270,8 +270,10 @@ static void test_sd_event_now(void) { assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + } assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); @@ -280,8 +282,10 @@ static void test_sd_event_now(void) { assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + } assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); } -- cgit v1.2.3-54-g00ecf From 8f1e0ad415dab8f3e5d301129fe2bb25c420e66f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 18:20:51 +0200 Subject: path-lookup: optimize a common strv copy operation away Follow-up for: https://github.com/systemd/systemd/pull/3033#discussion_r59689398 --- src/shared/path-lookup.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 80a2ea7940..ca593b6963 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -586,9 +586,16 @@ int lookup_paths_init( if (!add) return -ENOMEM; - r = strv_extend_strv(&paths, add, true); - if (r < 0) + if (paths) { + r = strv_extend_strv(&paths, add, true); + if (r < 0) return r; + } else { + /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, + * and don't have to copy anything */ + paths = add; + add = NULL; + } } r = patch_root_prefix(&persistent_config, root); -- cgit v1.2.3-54-g00ecf From f8591ee1b6c152055feed7a872159e67859fd83d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 18:28:32 +0200 Subject: systemctl: add two minor assert()s --- src/systemctl/systemctl.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9c59fcf610..595d6853c6 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -325,6 +325,8 @@ static int compare_unit_info(const void *a, const void *b) { } static bool output_show_unit(const UnitInfo *u, char **patterns) { + assert(u); + if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) return false; @@ -1281,6 +1283,8 @@ static int compare_unit_file_list(const void *a, const void *b) { } static bool output_show_unit_file(const UnitFileList *u, char **patterns) { + assert(u); + if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE)) return false; -- cgit v1.2.3-54-g00ecf From 03532f0ae0fae40f9f04091340e2bf156d0ec21a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 19:27:32 +0200 Subject: coredump,basic: generalize O_TMPFILE handling a bit This moves the O_TMPFILE handling from the coredumping code into common library code, and generalizes it as open_tmpfile_linkable() + link_tmpfile(). The existing open_tmpfile() function (which creates an unlinked temporary file that cannot be linked into the fs) is renamed to open_tmpfile_unlinkable(), to make the distinction clear. Thus, code may now choose between: a) open_tmpfile_linkable() + link_tmpfile() b) open_tmpfile_unlinkable() Depending on whether they want a file that may be linked back into the fs later on or not. In a later commit we should probably convert fopen_temporary() to make use of open_tmpfile_linkable(). Followup for: #3065 --- src/basic/fileio.c | 124 +++++++++++++++++++++++++++------- src/basic/fileio.h | 6 +- src/core/manager.c | 2 +- src/coredump/coredump.c | 54 +++------------ src/journal-remote/journal-gatewayd.c | 4 +- src/journal/journal-send.c | 2 +- src/journal/journal-verify.c | 6 +- src/test/test-tmpfiles.c | 26 +++++-- 8 files changed, 146 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 69590941e5..2a9b6e46ad 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1083,30 +1083,6 @@ int mkostemp_safe(char *pattern, int flags) { return fd; } -int open_tmpfile(const char *path, int flags) { - char *p; - int fd; - - assert(path); - -#ifdef O_TMPFILE - /* Try O_TMPFILE first, if it is supported */ - fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; -#endif - - /* Fall back to unguessable name + unlinking */ - p = strjoina(path, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p, flags); - if (fd < 0) - return fd; - - unlink(p); - return fd; -} - int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; @@ -1278,3 +1254,103 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) return fputs(s, f); } + +int open_tmpfile_unlinkable(const char *directory, int flags) { + char *p; + int fd; + + assert(directory); + + /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ + +#ifdef O_TMPFILE + /* Try O_TMPFILE first, if it is supported */ + fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); + if (fd >= 0) + return fd; +#endif + + /* Fall back to unguessable name + unlinking */ + p = strjoina(directory, "/systemd-tmp-XXXXXX"); + + fd = mkostemp_safe(p, flags); + if (fd < 0) + return fd; + + (void) unlink(p); + + return fd; +} + +int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { + _cleanup_free_ char *tmp = NULL; + int r, fd; + + assert(target); + assert(ret_path); + + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ + assert((flags & O_EXCL) == 0); + + /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in + * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in + * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ + +#ifdef O_TMPFILE + { + _cleanup_free_ char *dn = NULL; + + dn = dirname_malloc(target); + if (!dn) + return -ENOMEM; + + fd = open(dn, O_TMPFILE|flags, 0640); + if (fd >= 0) { + *ret_path = NULL; + return fd; + } + + log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); + } +#endif + + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return r; + + fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); + if (fd < 0) + return -errno; + + *ret_path = tmp; + tmp = NULL; + + return fd; +} + +int link_tmpfile(int fd, const char *path, const char *target) { + + assert(fd >= 0); + assert(target); + + /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd + * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported + * on the directory, and renameat2() is used instead. + * + * Note that in both cases we will not replace existing files. This is because linkat() dos not support this + * operation currently (renameat2() does), and there is no nice way to emulate this. */ + + if (path) { + if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) + return -errno; + } else { + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + + xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); + + if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + return -errno; + } + + return 0; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 8084895ff3..58dbc80c24 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -72,7 +72,6 @@ int fflush_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern, int flags); -int open_tmpfile(const char *path, int flags); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); @@ -82,3 +81,8 @@ int write_timestamp_file_atomic(const char *fn, usec_t n); int read_timestamp_file(const char *fn, usec_t *ret); int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); + +int open_tmpfile_unlinkable(const char *directory, int flags); +int open_tmpfile_linkable(const char *target, int flags, char **ret_path); + +int link_tmpfile(int fd, const char *path, const char *target); diff --git a/src/core/manager.c b/src/core/manager.c index 5601770670..bd00c224f4 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -2191,7 +2191,7 @@ int manager_open_serialization(Manager *m, FILE **_f) { assert(_f); path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; - fd = open_tmpfile(path, O_RDWR|O_CLOEXEC); + fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); if (fd < 0) return -errno; diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 2bbb958861..41fc1993d5 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -224,6 +224,8 @@ static int fix_permissions( const char *context[_CONTEXT_MAX], uid_t uid) { + int r; + assert(fd >= 0); assert(target); assert(context); @@ -236,18 +238,9 @@ static int fix_permissions( if (fsync(fd) < 0) return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); - if (filename) { - if (rename(filename, target) < 0) - return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target); - } else { - _cleanup_free_ char *proc_fd_path = NULL; - - if (asprintf(&proc_fd_path, "/proc/self/fd/%d", fd) < 0) - return log_oom(); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return log_error_errno(errno, "Failed to create coredump %s: %m", target); - } + r = link_tmpfile(fd, filename, target); + if (r < 0) + return log_error_errno(r, "Failed to move coredump %s into place: %m", target); return 0; } @@ -308,33 +301,6 @@ static int make_filename(const char *context[_CONTEXT_MAX], char **ret) { return 0; } -static int open_coredump_tmpfile(const char *target, char **ret_filename) { - _cleanup_free_ char *tmp = NULL; - int fd; - int r; - - assert(target); - assert(ret_filename); - - fd = open("/var/lib/systemd/coredump", O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0640); - if (fd < 0) { - log_debug_errno(errno, "Failed to use O_TMPFILE: %m"); - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return log_error_errno(r, "Failed to determine temporary file name: %m"); - - fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); - if (fd < 0) - return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); - } - - *ret_filename = tmp; - tmp = NULL; - - return fd; -} - static int save_external_coredump( const char *context[_CONTEXT_MAX], int input_fd, @@ -378,9 +344,9 @@ static int save_external_coredump( mkdir_p_label("/var/lib/systemd/coredump", 0755); - fd = open_coredump_tmpfile(fn, &tmp); + fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) - return fd; + return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r == -EFBIG) { @@ -418,9 +384,11 @@ static int save_external_coredump( goto uncompressed; } - fd_compressed = open_coredump_tmpfile(fn_compressed, &tmp_compressed); - if (fd_compressed < 0) + fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); + if (fd_compressed < 0) { + log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; + } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 60d897758b..4ad9184993 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -122,12 +122,14 @@ static int open_journal(RequestMeta *m) { } static int request_meta_ensure_tmp(RequestMeta *m) { + assert(m); + if (m->tmp) rewind(m->tmp); else { int fd; - fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC); + fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC); if (fd < 0) return fd; diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index a79846146a..f0959b6237 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -316,7 +316,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { buffer_fd = memfd_new(NULL); if (buffer_fd < 0) { if (buffer_fd == -ENOSYS) { - buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC); + buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); if (buffer_fd < 0) return buffer_fd; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index a1241c9bcf..26572ddd76 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -838,19 +838,19 @@ int journal_file_verify( } else if (f->seal) return -ENOKEY; - data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (data_fd < 0) { r = log_error_errno(data_fd, "Failed to create data file: %m"); goto fail; } - entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_fd < 0) { r = log_error_errno(entry_fd, "Failed to create entry file: %m"); goto fail; } - entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_array_fd < 0) { r = log_error_errno(entry_array_fd, "Failed to create entry array file: %m"); diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index d7223dd2bf..8fda0904f3 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -32,15 +32,17 @@ #include "util.h" int main(int argc, char** argv) { + _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL; + _cleanup_close_ int fd = -1, fd2 = -1, fd3 = -1; const char *p = argv[1] ?: "/tmp"; - char *pattern = strjoina(p, "/systemd-test-XXXXXX"); - _cleanup_close_ int fd, fd2; - _cleanup_free_ char *cmd, *cmd2, *ans, *ans2; + char *pattern; log_set_max_level(LOG_DEBUG); log_parse_environment(); - fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); + pattern = strjoina(p, "/systemd-test-XXXXXX"); + + fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); @@ -59,5 +61,21 @@ int main(int argc, char** argv) { log_debug("link2: %s", ans2); assert_se(endswith(ans2, " (deleted)")); + pattern = strjoina(p, "/tmpfiles-test"); + assert_se(tempfn_random(pattern, NULL, &d) >= 0); + + fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp); + assert_se(fd >= 0); + assert_se(write(fd, "foobar\n", 7) == 7); + + assert_se(touch(d) >= 0); + assert_se(link_tmpfile(fd, tmp, d) == -EEXIST); + assert_se(unlink(d) >= 0); + assert_se(link_tmpfile(fd, tmp, d) >= 0); + + assert_se(read_one_line_file(d, &line) >= 0); + assert_se(streq(line, "foobar")); + assert_se(unlink(d) >= 0); + return 0; } -- cgit v1.2.3-54-g00ecf From 766cd081525575875dd1291d4de0e65bcb5e1a89 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 19:37:46 +0200 Subject: shared: move output_mode_to_string() into output-mode.c After all, the enum definition is in output-mode.h --- Makefile.am | 1 + src/shared/logs-show.c | 15 --------------- src/shared/logs-show.h | 3 --- src/shared/output-mode.c | 36 ++++++++++++++++++++++++++++++++++++ src/shared/output-mode.h | 5 +++++ 5 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/shared/output-mode.c (limited to 'src') diff --git a/Makefile.am b/Makefile.am index b0118fd9a5..0f475c6d09 100644 --- a/Makefile.am +++ b/Makefile.am @@ -962,6 +962,7 @@ noinst_LTLIBRARIES += \ libshared_la_SOURCES = \ src/shared/output-mode.h \ + src/shared/output-mode.c \ src/shared/gpt.h \ src/shared/udev-util.h \ src/shared/linux/auto_dev-ioctl.h \ diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 15c818ccf1..895223a4d9 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1292,18 +1292,3 @@ int show_journal_by_unit( return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized); } - -static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { - [OUTPUT_SHORT] = "short", - [OUTPUT_SHORT_ISO] = "short-iso", - [OUTPUT_SHORT_PRECISE] = "short-precise", - [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", - [OUTPUT_VERBOSE] = "verbose", - [OUTPUT_EXPORT] = "export", - [OUTPUT_JSON] = "json", - [OUTPUT_JSON_PRETTY] = "json-pretty", - [OUTPUT_JSON_SSE] = "json-sse", - [OUTPUT_CAT] = "cat" -}; - -DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 9765a24ff2..6643440881 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -68,6 +68,3 @@ void json_escape( const char* p, size_t l, OutputFlags flags); - -const char* output_mode_to_string(OutputMode m) _const_; -OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c new file mode 100644 index 0000000000..be6281bd3f --- /dev/null +++ b/src/shared/output-mode.c @@ -0,0 +1,36 @@ +/*** + 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 . +***/ + +#include "output-mode.h" +#include "string-table.h" + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_ISO] = "short-iso", + [OUTPUT_SHORT_PRECISE] = "short-precise", + [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_VERBOSE] = "verbose", + [OUTPUT_EXPORT] = "export", + [OUTPUT_JSON] = "json", + [OUTPUT_JSON_PRETTY] = "json-pretty", + [OUTPUT_JSON_SSE] = "json-sse", + [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index e2b26e04d3..ea2e95f06b 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -19,6 +19,8 @@ along with systemd; If not, see . ***/ +#include "macro.h" + typedef enum OutputMode { OUTPUT_SHORT, OUTPUT_SHORT_ISO, @@ -48,3 +50,6 @@ typedef enum OutputFlags { OUTPUT_UTC = 1 << 7, OUTPUT_KERNEL_THREADS = 1 << 8, } OutputFlags; + +const char* output_mode_to_string(OutputMode m) _const_; +OutputMode output_mode_from_string(const char *s) _pure_; -- cgit v1.2.3-54-g00ecf From bb321ed9a33d6c90e2529db86ffabd913805088a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 19:46:37 +0200 Subject: journalctl: add output mode where time is shown in seconds since 1st Jan 1970 UTC aka "UNIX time". Fixes: #2120 --- man/journalctl.xml | 10 ++++++++++ src/shared/logs-show.c | 13 ++++++++++--- src/shared/output-mode.c | 1 + src/shared/output-mode.h | 1 + 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/man/journalctl.xml b/man/journalctl.xml index 7a634879cc..c448e0771b 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -270,6 +270,16 @@ + + + + + + is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock + timestamps ("UNIX time"). The time is shown with microsecond accuracy. + + + diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 895223a4d9..5cfa455e05 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -344,16 +344,22 @@ static int output_short( t = (time_t) (x / USEC_PER_SEC); - switch(mode) { + switch (mode) { + + case OUTPUT_SHORT_UNIX: + r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); + break; + case OUTPUT_SHORT_ISO: r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); break; + case OUTPUT_SHORT_PRECISE: r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); if (r > 0) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); break; + default: r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); } @@ -894,6 +900,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( [OUTPUT_SHORT_ISO] = output_short, [OUTPUT_SHORT_PRECISE] = output_short, [OUTPUT_SHORT_MONOTONIC] = output_short, + [OUTPUT_SHORT_UNIX] = output_short, [OUTPUT_VERBOSE] = output_verbose, [OUTPUT_EXPORT] = output_export, [OUTPUT_JSON] = output_json, diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c index be6281bd3f..bec53ee0ae 100644 --- a/src/shared/output-mode.c +++ b/src/shared/output-mode.c @@ -25,6 +25,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { [OUTPUT_SHORT_ISO] = "short-iso", [OUTPUT_SHORT_PRECISE] = "short-precise", [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_SHORT_UNIX] = "short-unix", [OUTPUT_VERBOSE] = "verbose", [OUTPUT_EXPORT] = "export", [OUTPUT_JSON] = "json", diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index ea2e95f06b..56fd3ba8e3 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -26,6 +26,7 @@ typedef enum OutputMode { OUTPUT_SHORT_ISO, OUTPUT_SHORT_PRECISE, OUTPUT_SHORT_MONOTONIC, + OUTPUT_SHORT_UNIX, OUTPUT_VERBOSE, OUTPUT_EXPORT, OUTPUT_JSON, -- cgit v1.2.3-54-g00ecf From 991e274b61f489e625274254138567c9c81b4c4b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Apr 2016 20:09:57 +0200 Subject: journalctl: add --no-hostname switch This suppresses output of the hostname for messages from the local system. Fixes: #2342 --- man/journalctl.xml | 7 +++++++ src/journal/journalctl.c | 11 ++++++++++- src/shared/logs-show.c | 6 ++++++ src/shared/output-mode.h | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/man/journalctl.xml b/man/journalctl.xml index c448e0771b..3efe6ef62a 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -359,6 +359,13 @@ (UTC). + + + + Don't show the hostname field of log messages originating from the local host. This switch only + has an effect on the family of output modes (see above). + + diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index fd2cb99410..c9a2c3812d 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -95,6 +95,7 @@ static bool arg_boot = false; static sd_id128_t arg_boot_id = {}; static int arg_boot_offset = 0; static bool arg_dmesg = false; +static bool arg_no_hostname = false; static const char *arg_cursor = NULL; static const char *arg_after_cursor = NULL; static bool arg_show_cursor = false; @@ -304,6 +305,7 @@ static void help(void) { " -a --all Show all fields, including long and unprintable\n" " -q --quiet Do not show info messages and privilege warning\n" " --no-pager Do not pipe output into a pager\n" + " --no-hostname Suppress output of hostname field\n" " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" @@ -370,6 +372,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VACUUM_SIZE, ARG_VACUUM_FILES, ARG_VACUUM_TIME, + ARG_NO_HOSTNAME, }; static const struct option options[] = { @@ -427,6 +430,7 @@ static int parse_argv(int argc, char *argv[]) { { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, + { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, {} }; @@ -780,6 +784,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_LIST_FIELD_NAMES; break; + case ARG_NO_HOSTNAME: + arg_no_hostname = true; + break; + case 'x': arg_catalog = true; break; @@ -2444,7 +2452,8 @@ int main(int argc, char *argv[]) { arg_full * OUTPUT_FULL_WIDTH | colors_enabled() * OUTPUT_COLOR | arg_catalog * OUTPUT_CATALOG | - arg_utc * OUTPUT_UTC; + arg_utc * OUTPUT_UTC | + arg_no_hostname * OUTPUT_NO_HOSTNAME; r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized); need_seek = true; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 5cfa455e05..cd3d53669c 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -373,6 +373,12 @@ static int output_short( n += strlen(buf); } + if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { + /* Suppress display of the hostname if this is requested. */ + hostname = NULL; + hostname_len = 0; + } + if (hostname && shall_print(hostname, hostname_len, flags)) { fprintf(f, " %.*s", (int) hostname_len, hostname); n += hostname_len + 1; diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index 56fd3ba8e3..f37189e57f 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -50,6 +50,7 @@ typedef enum OutputFlags { OUTPUT_BEGIN_NEWLINE = 1 << 6, OUTPUT_UTC = 1 << 7, OUTPUT_KERNEL_THREADS = 1 << 8, + OUTPUT_NO_HOSTNAME = 1 << 9, } OutputFlags; const char* output_mode_to_string(OutputMode m) _const_; -- cgit v1.2.3-54-g00ecf