ask_password_agent_open();
}
+#ifdef HAVE_LOGIND
static void polkit_agent_open_if_enabled(void) {
/* Open the polkit agent as a child process if necessary */
polkit_agent_open();
}
+#endif
static const char *ansi_highlight_red(bool b) {
return r;
}
+static int get_unit_path(
+ DBusConnection *bus,
+ DBusError *error,
+ const char *name,
+ char **unit_path) {
+
+ DBusMessage *m = NULL, *reply = NULL;
+ int r = 0;
+
+ assert(bus);
+ assert(error);
+ assert(name);
+ assert(unit_path);
+
+ *unit_path = NULL;
+
+
+ m = dbus_message_new_method_call("org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnit");
+ if (!m) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ if (streq(error->name, BUS_ERROR_NO_SUCH_UNIT)) {
+ dbus_error_free(error);
+ r = -EINVAL;
+ } else {
+ log_error("Failed to issue method call: %s", bus_error_message(error));
+ r = -EIO;
+ }
+ goto finish;
+ }
+
+ if (!dbus_message_get_args(reply, error,
+ DBUS_TYPE_OBJECT_PATH, unit_path,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(error));
+ r = -EIO;
+ goto finish;
+ }
+
+ *unit_path = strdup(*unit_path);
+ if (!(*unit_path)) {
+ log_error("Failed to duplicate unit path");
+ r = -ENOMEM;
+ }
+finish:
+ if (m)
+ dbus_message_unref(m);
+ if (reply)
+ dbus_message_unref(reply);
+ return r;
+}
+
+static int is_socket_listening(
+ DBusConnection *bus,
+ DBusError *error,
+ const char *socket_name) {
+
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusMessageIter iter, sub;
+ char *socket_object_path = NULL;
+ const char *sub_state = NULL,
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "SubState";
+ int r = 0;
+
+ assert(bus);
+ assert(error);
+ assert(socket_name);
+
+ if ((r = get_unit_path(bus, error, socket_name, &socket_object_path)) < 0) {
+ goto finish;
+ }
+ m = dbus_message_new_method_call("org.freedesktop.systemd1",
+ socket_object_path,
+ "org.freedesktop.DBus.Properties",
+ "Get");
+ if (!m) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(error));
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply: %s", bus_error_message(error));
+ r = -EIO;
+ goto finish;
+ }
+ dbus_message_iter_get_basic(&sub, &sub_state);
+ r = streq(sub_state, "listening");
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ free(socket_object_path);
+ return r;
+}
+
+static void check_listening_sockets(
+ DBusConnection *bus,
+ DBusError *error,
+ const char *unit_name) {
+
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusMessageIter iter, sub;
+ const char *service_trigger = NULL,
+ *interface = "org.freedesktop.systemd1.Unit",
+ *triggered_by_property = "TriggeredBy";
+
+ char *unit_path = NULL;
+ int print_warning_label = 1;
+
+ if ((get_unit_path(bus, error, unit_name, &unit_path) < 0)) {
+ goto finish;
+ }
+
+ m = dbus_message_new_method_call("org.freedesktop.systemd1",
+ unit_path,
+ "org.freedesktop.DBus.Properties",
+ "Get");
+ if (!m) {
+ log_error("Could not allocate message.");
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &triggered_by_property,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(error));
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply: %s", bus_error_message(error));
+ goto finish;
+
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_recurse(&sub, &iter);
+ sub = iter;
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ int r = 0;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply: %s", bus_error_message(error));
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, &service_trigger);
+
+ if (endswith(service_trigger, ".socket")) {
+ r = is_socket_listening(bus, error, service_trigger);
+ } else {
+ dbus_message_iter_recurse(&iter, &sub);
+ iter = sub;
+ continue;
+ }
+
+ if (r == 1) {
+ if (print_warning_label) {
+ log_warning("There are listening sockets associated with %s :", unit_name);
+ print_warning_label = 0;
+ }
+ log_warning("%s",service_trigger);
+ } else if (r < 0) {
+ log_error("Failed to issue function call: %s", bus_error_message(error));
+ goto finish;
+ }
+ dbus_message_iter_recurse(&iter, &sub);
+ iter = sub;
+ }
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ free(unit_path);
+}
+
static int start_unit_one(
DBusConnection *bus,
const char *method,
}
}
+ /* When stopping unit check if we have some listening sockets active */
+ if (streq(method, "StopUnit") && !arg_quiet) {
+ check_listening_sockets(bus, error, name);
+ }
+
r = 0;
finish:
const char *description;
const char *following;
- const char *path;
+ char **documentation;
+
+ const char *fragment_path;
+ const char *source_path;
const char *default_control_group;
const char *load_error;
pid_t control_pid;
const char *status_text;
bool running:1;
-#ifdef HAVE_SYSV_COMPAT
- bool is_sysv:1;
-#endif
usec_t start_timestamp;
usec_t exit_timestamp;
usec_t timestamp;
char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
+ const char *path;
assert(i);
} else
on = off = "";
+ path = i->source_path ? i->source_path : i->fragment_path;
+
if (i->load_error)
printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
- else if (i->path && i->unit_file_state)
- printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
- else if (i->path)
- printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
+ else if (path && i->unit_file_state)
+ printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, path, i->unit_file_state);
+ else if (path)
+ printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, path);
else
printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
if (i->what)
printf("\t What: %s\n", i->what);
+ if (!strv_isempty(i->documentation)) {
+ char **t;
+ bool first = true;
+
+ STRV_FOREACH(t, i->documentation) {
+ if (first) {
+ printf("\t Docs: %s\n", *t);
+ first = false;
+ } else
+ printf("\t %s\n", *t);
+ }
+ }
+
if (i->accept)
printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
free(t);
-#ifdef HAVE_SYSV_COMPAT
- if (i->is_sysv)
- good = is_clean_exit_lsb(p->code, p->status);
- else
-#endif
- good = is_clean_exit(p->code, p->status);
-
+ good = is_clean_exit_lsb(p->code, p->status);
if (!good) {
on = ansi_highlight_red(true);
off = ansi_highlight_red(false);
printf("status=%i", p->status);
-#ifdef HAVE_SYSV_COMPAT
- if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
- if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
-#endif
+ c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
+ if (c)
printf("/%s", c);
} else
printf("status=%i", i->exit_status);
-#ifdef HAVE_SYSV_COMPAT
- if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
- if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
-#endif
+ c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
+ if (c)
printf("/%s", c);
} else
arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
}
+static void show_unit_help(UnitStatusInfo *i) {
+ char **p;
+
+ assert(i);
+
+ if (!i->documentation) {
+ log_info("Documentation for %s not known.", i->id);
+ return;
+ }
+
+ STRV_FOREACH(p, i->documentation) {
+
+ if (startswith(*p, "man:")) {
+ size_t k;
+ char *e = NULL;
+ char *page = NULL, *section = NULL;
+ const char *args[4] = { "man", NULL, NULL, NULL };
+ pid_t pid;
+
+ k = strlen(*p);
+
+ if ((*p)[k-1] == ')')
+ e = strrchr(*p, '(');
+
+ if (e) {
+ page = strndup((*p) + 4, e - *p - 4);
+ if (!page) {
+ log_error("Out of memory.");
+ return;
+ }
+
+ section = strndup(e + 1, *p + k - e - 2);
+ if (!section) {
+ free(page);
+ log_error("Out of memory");
+ return;
+ }
+
+ args[1] = section;
+ args[2] = page;
+ } else
+ args[1] = *p + 4;
+
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ free(page);
+ free(section);
+ continue;
+ }
+
+ if (pid == 0) {
+ /* Child */
+ execvp(args[0], (char**) args);
+ log_error("Failed to execute man: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ free(page);
+ free(section);
+
+ wait_for_terminate(pid, NULL);
+ } else
+ log_info("Can't show %s.", *p);
+ }
+}
+
static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
assert(name);
else if (streq(name, "Description"))
i->description = s;
else if (streq(name, "FragmentPath"))
- i->path = s;
-#ifdef HAVE_SYSV_COMPAT
- else if (streq(name, "SysVPath")) {
- i->is_sysv = true;
- i->path = s;
- }
-#endif
+ i->fragment_path = s;
+ else if (streq(name, "SourcePath"))
+ i->source_path = s;
else if (streq(name, "DefaultControlGroup"))
i->default_control_group = s;
else if (streq(name, "StatusText"))
LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
+ dbus_message_iter_next(&sub);
+ }
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING &&
+ streq(name, "Documentation")) {
+
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ const char *s;
+ char **l;
+
+ dbus_message_iter_get_basic(&sub, &s);
+
+ l = strv_append(i->documentation, s);
+ if (!l)
+ return -ENOMEM;
+
+ strv_free(i->documentation);
+ i->documentation = l;
+
dbus_message_iter_next(&sub);
}
}
r = 0;
- if (!show_properties)
- print_status_info(&info);
+ if (!show_properties) {
+ if (streq(verb, "help"))
+ show_unit_help(&info);
+ else
+ print_status_info(&info);
+ }
+
+ strv_free(info.documentation);
if (!streq_ptr(info.active_state, "active") &&
!streq_ptr(info.active_state, "reloading") &&
assert(bus);
assert(args);
- show_properties = !streq(args[0], "status");
+ show_properties = streq(args[0], "show");
if (show_properties)
pager_open_if_enabled();
* afterwards only the native units remain */
zero(paths);
- r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+ r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, NULL, NULL, NULL);
if (r < 0)
return r;
r = 0;
-
for (f = 1; args[f]; f++) {
const char *name;
char *p;
" status [NAME...|PID...] Show runtime status of one or more units\n"
" show [NAME...|JOB...] Show properties of one or more\n"
" units/jobs or the manager\n"
+ " help [NAME...|PID...] Show manual for one or more units\n"
" reset-failed [NAME...] Reset failed state for all, one, or more\n"
" units\n"
" load [NAME...] Load one or more units\n\n"
" -H --halt Halt the machine\n"
" -P --poweroff Power-off the machine\n"
" -r --reboot Reboot the machine\n"
- " -h Equivalent to --poweroff, overriden by --halt\n"
+ " -h Equivalent to --poweroff, overridden by --halt\n"
" -k Don't halt/power-off/reboot, just send warnings\n"
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" -c Cancel a pending shutdown\n",
{ "check", MORE, 2, check_unit },
{ "show", MORE, 1, show },
{ "status", MORE, 2, show },
+ { "help", MORE, 2, show },
{ "dump", EQUAL, 1, dump },
{ "dot", EQUAL, 1, dot },
{ "snapshot", LESS, 2, snapshot },
/* Special rule: no arguments means "list-units" */
i = 0;
else {
- if (streq(argv[optind], "help")) {
- systemctl_help();
- return 0;
+ if (streq(argv[optind], "help") && !argv[optind+1]) {
+ log_error("This command expects one or more "
+ "unit names. Did you mean --help?");
+ return -EINVAL;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)