#include "path-lookup.h"
#include "conf-parser.h"
#include "exit-status.h"
-#include "bus-errors.h"
#include "build.h"
#include "unit-name.h"
#include "pager.h"
#include "logs-show.h"
#include "socket-util.h"
#include "fileio.h"
+#include "env-util.h"
#include "bus-util.h"
#include "bus-message.h"
#include "bus-error.h"
static unsigned arg_lines = 10;
static OutputMode arg_output = OUTPUT_SHORT;
static bool arg_plain = false;
+static const struct {
+ const char *verb;
+ const char *method;
+} unit_actions[] = {
+ { "start", "StartUnit" },
+ { "stop", "StopUnit" },
+ { "condstop", "StopUnit" },
+ { "reload", "ReloadUnit" },
+ { "restart", "RestartUnit" },
+ { "try-restart", "TryRestartUnit" },
+ { "condrestart", "TryRestartUnit" },
+ { "reload-or-restart", "ReloadOrRestartUnit" },
+ { "reload-or-try-restart", "ReloadOrTryRestartUnit" },
+ { "condreload", "ReloadOrTryRestartUnit" },
+ { "force-reload", "ReloadOrTryRestartUnit" }
+};
static int daemon_reload(sd_bus *bus, char **args);
static int halt_now(enum action a);
return 0;
}
+static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
+ usec_t next_elapse;
+
+ assert(nw);
+ assert(next);
+
+ if (next->monotonic != (usec_t) -1 && next->monotonic > 0) {
+ usec_t converted;
+
+ if (next->monotonic > nw->monotonic)
+ converted = nw->realtime + (next->monotonic - nw->monotonic);
+ else
+ converted = nw->realtime - (nw->monotonic - next->monotonic);
+
+ if (next->realtime != (usec_t) -1 && next->realtime > 0)
+ next_elapse = MIN(converted, next->realtime);
+ else
+ next_elapse = converted;
+
+ } else
+ next_elapse = next->realtime;
+
+ return next_elapse;
+}
+
static int list_timers(sd_bus *bus, char **args) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
for (u = unit_infos; u < unit_infos + n; u++) {
_cleanup_strv_free_ char **triggered = NULL;
- dual_timestamp next;
+ dual_timestamp next = {};
usec_t m;
if (!endswith(u->id, ".timer"))
if (r < 0)
goto cleanup;
- if (next.monotonic != (usec_t) -1 && next.monotonic > 0) {
- usec_t converted;
-
- if (next.monotonic > nw.monotonic)
- converted = nw.realtime + (next.monotonic - nw.monotonic);
- else
- converted = nw.realtime - (nw.monotonic - next.monotonic);
-
- if (next.realtime != (usec_t) -1 && next.realtime > 0)
- m = MIN(converted, next.realtime);
- else
- m = converted;
- } else
- m = next.realtime;
-
if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
r = log_oom();
goto cleanup;
}
+ m = calc_next_elapse(&nw, &next);
+
timer_infos[c++] = (struct timer_info) {
.id = u->id,
.next_elapse = m,
char ***units,
unsigned int branches) {
- _cleanup_strv_free_ char **deps = NULL, **u;
+ _cleanup_strv_free_ char **deps = NULL;
char **c;
int r = 0;
assert(name);
assert(units);
- u = strv_append(*units, name);
- if (!u)
+ r = strv_extend(units, name);
+ if (r < 0)
return log_oom();
r = list_dependencies_get_dependencies(bus, name, &deps);
STRV_FOREACH(c, deps) {
int state;
- if (strv_contains(u, *c)) {
+ if (strv_contains(*units, *c)) {
if (!arg_plain) {
r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1);
if (r < 0)
return r;
if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) {
- r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1));
+ r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1));
if (r < 0)
return r;
}
}
- if (arg_plain) {
- strv_free(*units);
- *units = u;
- u = NULL;
- }
+ if (!arg_plain)
+ strv_remove(*units, name);
return 0;
}
return 0;
}
+static const char *verb_to_method(const char *verb) {
+ uint i;
+
+ for (i = 0; i < ELEMENTSOF(unit_actions); i++)
+ if (streq_ptr(unit_actions[i].verb, verb))
+ return unit_actions[i].method;
+
+ return "StartUnit";
+}
+
+static const char *method_to_verb(const char *method) {
+ uint i;
+
+ for (i = 0; i < ELEMENTSOF(unit_actions); i++)
+ if (streq_ptr(unit_actions[i].method, method))
+ return unit_actions[i].verb;
+
+ return "n/a";
+}
+
static int start_unit_one(
sd_bus *bus,
const char *method,
&reply,
"ss", name, mode);
if (r < 0) {
+ const char *verb;
+
if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
/* There's always a fallback possible for
* legacy actions. */
return -EADDRNOTAVAIL;
- log_error("Failed to %s %s: %s", method, name, bus_error_message(error, r));
+ verb = method_to_verb(method);
+
+ log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r));
return r;
}
if (arg_action == ACTION_SYSTEMCTL) {
enum action action;
- method =
- streq(args[0], "stop") ||
- streq(args[0], "condstop") ? "StopUnit" :
- streq(args[0], "reload") ? "ReloadUnit" :
- streq(args[0], "restart") ? "RestartUnit" :
-
- streq(args[0], "try-restart") ||
- streq(args[0], "condrestart") ? "TryRestartUnit" :
-
- streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" :
-
- streq(args[0], "reload-or-try-restart") ||
- streq(args[0], "condreload") ||
- streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" :
- "StartUnit";
+ method = verb_to_method(args[0]);
action = verb_to_action(args[0]);
mode = streq(args[0], "isolate") ? "isolate" :
static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) {
_cleanup_strv_free_ char **names = NULL;
char **name;
- int r = code;
+ int r;
assert(bus);
assert(args);
r = expand_names(bus, args, NULL, &names);
- if (r < 0)
+ if (r < 0) {
log_error("Failed to expand names: %s", strerror(-r));
+ return r;
+ }
STRV_FOREACH(name, names) {
int state;
state = check_one_unit(bus, *name, good_states, arg_quiet);
if (state < 0)
return state;
- if (state > 0)
- r = 0;
+ if (state == 0)
+ r = code;
}
return r;
if (arg_all || !isempty(a) || !isempty(b))
printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+ return 0;
+ } else if (streq_ptr(name, "SystemCallFilter")) {
+ _cleanup_strv_free_ char **l = NULL;
+ int whitelist;
+
+ r = sd_bus_message_enter_container(m, 'r', "bas");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(m, "b", &whitelist);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_strv(m, &l);
+ 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);
+
+ if (arg_all || whitelist || !strv_isempty(l)) {
+ bool first = true;
+ char **i;
+
+ fputs(name, stdout);
+ fputc('=', stdout);
+
+ if (!whitelist)
+ fputc('~', stdout);
+
+ STRV_FOREACH(i, l) {
+ if (first)
+ first = false;
+ else
+ fputc(' ', stdout);
+
+ fputs(*i, stdout);
+ }
+ fputc('\n', stdout);
+ }
+
return 0;
}
streq(verb, "status")) {
/* According to LSB: "program not running" */
/* 0: program is running or service is OK
- * 1: program is dead and /var/run pid file exists
- * 2: program is dead and /var/lock lock file exists
+ * 1: program is dead and /run PID file exists
+ * 2: program is dead and /run/lock lock file exists
* 3: program is not running
* 4: program or service status is unknown
*/
} else if (streq(field, "MemoryLimit")) {
off_t bytes;
- r = parse_bytes(eq, &bytes);
+ r = parse_size(eq, 1024, &bytes);
if (r < 0) {
log_error("Failed to parse bytes specification %s", assignment);
return -EINVAL;
return -EINVAL;
}
- r = parse_bytes(bandwidth, &bytes);
+ r = parse_size(bandwidth, 1000, &bytes);
if (r < 0) {
log_error("Failed to parse byte value %s.", bandwidth);
return -EINVAL;
r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "SetUnitProperties",
- &m);
+ "SetUnitProperties");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method,
- &m);
+ method);
if (r < 0)
return bus_log_create_error(r);
return 0;
}
+static int import_environment(sd_bus *bus, char **args) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ assert(bus);
+ assert(args);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "SetEnvironment");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ if (strv_isempty(args + 1))
+ r = sd_bus_message_append_strv(m, environ);
+ else {
+ char **a, **b;
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ STRV_FOREACH(a, args + 1) {
+
+ if (!env_name_is_valid(*a)) {
+ log_error("Not a valid environment variable name: %s", *a);
+ return -EINVAL;
+ }
+
+ STRV_FOREACH(b, environ) {
+ const char *eq;
+
+ eq = startswith(*b, *a);
+ if (eq && *eq == '=') {
+
+ r = sd_bus_message_append(m, "s", *b);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ break;
+ }
+ }
+ }
+
+ r = sd_bus_message_close_container(m);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, NULL);
+ if (r < 0) {
+ log_error("Failed to import environment: %s", bus_error_message(&error, r));
+ return r;
+ }
+
+ return 0;
+}
+
static int enable_sysv_units(const char *verb, char **args) {
int r = 0;
if (r < 0)
return r;
+ /* If the operation was fully executed by the SysV compat,
+ * let's finish early */
+ if (strv_isempty(names))
+ return 0;
+
if (!bus || avoid_bus()) {
if (streq(verb, "enable")) {
r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- method,
- &m);
+ method);
if (r < 0)
return bus_log_create_error(r);
"Environment Commands:\n"
" show-environment Dump environment\n"
" set-environment NAME=VALUE... Set one or more environment variables\n"
- " unset-environment NAME... Unset one or more environment variables\n\n"
+ " unset-environment NAME... Unset one or more environment variables\n"
+ " import-environment NAME... Import all, one or more environment variables\n\n"
"Manager Lifecycle Commands:\n"
" daemon-reload Reload systemd manager configuration\n"
" daemon-reexec Reexecute systemd manager\n\n"
case ARG_HELP:
return runlevel_help();
- return 0;
case '?':
return -EINVAL;
{ "show-environment", EQUAL, 1, show_environment },
{ "set-environment", MORE, 2, set_environment },
{ "unset-environment", MORE, 2, set_environment },
+ { "import-environment", MORE, 1, import_environment},
{ "halt", EQUAL, 1, start_special, FORCE },
{ "poweroff", EQUAL, 1, start_special, FORCE },
{ "reboot", EQUAL, 1, start_special, FORCE },