chiark / gitweb /
systemctl: style fixes for the previous patch
[elogind.git] / src / systemctl / systemctl.c
index 58b6035c690c68d8748fc41b9b883adf335b8844..f2da2ad604278d5bf26d7ec7ed33aee5e12a8a13 100644 (file)
@@ -1500,6 +1500,238 @@ finish:
         return r;
 }
 
+static int get_unit_path(
+                DBusConnection *bus,
+                const char *name,
+                char **unit_path) {
+
+        DBusError error;
+        DBusMessage *m = NULL, *reply = NULL;
+        char *path;
+        int r = 0;
+
+        assert(bus);
+        assert(name);
+        assert(unit_path);
+
+        dbus_error_init(&error);
+
+        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))
+                        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, &path,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        *unit_path = strdup(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);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int is_socket_listening(
+                DBusConnection *bus,
+                const char *socket_name) {
+
+        DBusError error;
+        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(socket_name);
+
+        dbus_error_init(&error);
+
+        r = get_unit_path(bus, socket_name, &socket_object_path);
+        if (r < 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);
+
+        dbus_error_free(&error);
+
+        free(socket_object_path);
+        return r;
+}
+
+static void check_listening_sockets(
+                DBusConnection *bus,
+                const char *unit_name) {
+
+        DBusError error;
+        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;
+        bool print_warning_label = true;
+
+        dbus_error_init(&error);
+
+        if (get_unit_path(bus, 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;
+
+                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"))
+                        goto next;
+
+                r = is_socket_listening(bus, service_trigger);
+                if (r < 0)
+                        goto finish;
+                if (r == 1) {
+                        if (print_warning_label) {
+                                log_warning("There are listening sockets associated with %s :", unit_name);
+                                print_warning_label = false;
+                        }
+                        log_warning("%s", service_trigger);
+                }
+next:
+                dbus_message_iter_recurse(&iter, &sub);
+                iter = sub;
+        }
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        free(unit_path);
+}
+
 static int start_unit_one(
                 DBusConnection *bus,
                 const char *method,
@@ -1580,6 +1812,10 @@ static int start_unit_one(
                 }
         }
 
+        /* When stopping unit check if we have some listening sockets active */
+        if (streq(method, "StopUnit") && !arg_quiet)
+                check_listening_sockets(bus, name);
+
         r = 0;
 
 finish:
@@ -2458,6 +2694,73 @@ static void print_status_info(UnitStatusInfo *i) {
                        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);
@@ -2952,8 +3255,12 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
 
         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);
 
@@ -3044,7 +3351,7 @@ static int show(DBusConnection *bus, char **args) {
         assert(bus);
         assert(args);
 
-        show_properties = !streq(args[0], "status");
+        show_properties = streq(args[0], "show");
 
         if (show_properties)
                 pager_open_if_enabled();
@@ -4213,6 +4520,7 @@ static int systemctl_help(void) {
                "  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"
@@ -5176,6 +5484,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "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          },
@@ -5220,9 +5529,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 /* 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++)