]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/login/loginctl.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / login / loginctl.c
index 02d240c704de6cae51eecb3fb7adfec87fe81643..92443c819098846242b4221719c1893628a787e9 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <unistd.h>
 #include <errno.h>
-#include <string.h>
 #include <getopt.h>
 #include <locale.h>
+#include <string.h>
+#include <unistd.h>
 
 #include "sd-bus.h"
-#include "bus-util.h"
+
 #include "bus-error.h"
+#include "bus-util.h"
+#include "cgroup-show.h"
+#include "cgroup-util.h"
 #include "log.h"
-#include "util.h"
+#include "logs-show.h"
 #include "macro.h"
 #include "pager.h"
-#include "build.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "spawn-polkit-agent.h"
 #include "strv.h"
-#include "unit-name.h"
 #include "sysfs-show.h"
-#include "logs-show.h"
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "spawn-polkit-agent.h"
-#include "verbs.h"
-#include "process-util.h"
 #include "terminal-util.h"
-#include "signal-util.h"
+#include "unit-name.h"
+#include "user-util.h"
+#include "util.h"
+#include "verbs.h"
 
 static char **arg_property = NULL;
 static bool arg_all = false;
@@ -263,7 +265,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
         if (isempty(cgroup))
                 return 0;
 
-        if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
+        if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
                 return 0;
 
         c = columns();
@@ -277,42 +279,81 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
 }
 
 typedef struct SessionStatusInfo {
-        const char *id;
+        char *id;
         uid_t uid;
-        const char *name;
+        char *name;
         struct dual_timestamp timestamp;
         unsigned int vtnr;
-        const char *seat;
-        const char *tty;
-        const char *display;
+        char *seat;
+        char *tty;
+        char *display;
         bool remote;
-        const char *remote_host;
-        const char *remote_user;
-        const char *service;
+        char *remote_host;
+        char *remote_user;
+        char *service;
         pid_t leader;
-        const char *type;
-        const char *class;
-        const char *state;
-        const char *scope;
-        const char *desktop;
+        char *type;
+        char *class;
+        char *state;
+        char *scope;
+        char *desktop;
 } SessionStatusInfo;
 
 typedef struct UserStatusInfo {
         uid_t uid;
-        const char *name;
+        char *name;
         struct dual_timestamp timestamp;
-        const char *state;
+        char *state;
         char **sessions;
-        const char *display;
-        const char *slice;
+        char *display;
+        char *slice;
 } UserStatusInfo;
 
 typedef struct SeatStatusInfo {
-        const char *id;
-        const char *active_session;
+        char *id;
+        char *active_session;
         char **sessions;
 } SeatStatusInfo;
 
+static void session_status_info_clear(SessionStatusInfo *info) {
+        if (info) {
+                free(info->id);
+                free(info->name);
+                free(info->seat);
+                free(info->tty);
+                free(info->display);
+                free(info->remote_host);
+                free(info->remote_user);
+                free(info->service);
+                free(info->type);
+                free(info->class);
+                free(info->state);
+                free(info->scope);
+                free(info->desktop);
+                zero(*info);
+        }
+}
+
+static void user_status_info_clear(UserStatusInfo *info) {
+        if (info) {
+                free(info->name);
+                free(info->state);
+                strv_free(info->sessions);
+                free(info->display);
+                free(info->slice);
+                zero(*info);
+        }
+}
+
+static void seat_status_info_clear(SeatStatusInfo *info) {
+        if (info) {
+                free(info->id);
+                free(info->active_session);
+                strv_free(info->sessions);
+                zero(*info);
+        }
+}
+
 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
         const char *contents;
         int r;
@@ -333,11 +374,9 @@ static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_mess
                 if (r < 0)
                         return r;
 
-                free(*p);
-                *p = strdup(s);
-
-                if (!*p)
-                        return -ENOMEM;
+                r = free_and_strdup(p, s);
+                if (r < 0)
+                        return r;
         } else {
                 r = sd_bus_message_read_basic(m, contents[0], userdata);
                 if (r < 0)
@@ -404,7 +443,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
 
         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
         char since2[FORMAT_TIMESTAMP_MAX], *s2;
-        SessionStatusInfo i = {};
+        _cleanup_(session_status_info_clear) SessionStatusInfo i = {};
         int r;
 
         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
@@ -532,14 +571,12 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
 
         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
         char since2[FORMAT_TIMESTAMP_MAX], *s2;
-        UserStatusInfo i = {};
+        _cleanup_(user_status_info_clear) UserStatusInfo i = {};
         int r;
 
         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
-        if (r < 0) {
-                log_error_errno(r, "Could not get properties: %m");
-                goto finish;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not get properties: %m");
 
         if (*new_line)
                 printf("\n");
@@ -594,10 +631,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
                                 NULL);
         }
 
-finish:
-        strv_free(i.sessions);
-
-        return r;
+        return 0;
 }
 
 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
@@ -609,14 +643,12 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
                 {}
         };
 
-        SeatStatusInfo i = {};
+        _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
         int r;
 
         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
-        if (r < 0) {
-                log_error_errno(r, "Could not get properties: %m");
-                goto finish;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Could not get properties: %m");
 
         if (*new_line)
                 printf("\n");
@@ -653,25 +685,168 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
                 show_sysfs(i.id, "\t\t  ", c);
         }
 
-finish:
-        strv_free(i.sessions);
+        return 0;
+}
 
-        return r;
+static int print_property(const char *name, sd_bus_message *m, const char *contents) {
+        int r;
+
+        assert(name);
+        assert(m);
+        assert(contents);
+
+        if (arg_property && !strv_find(arg_property, name))
+                /* skip what we didn't read */
+                return sd_bus_message_skip(m, contents);
+
+        switch (contents[0]) {
+
+        case SD_BUS_TYPE_STRUCT_BEGIN:
+
+                if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
+                        const char *s;
+
+                        r = sd_bus_message_read(m, "(so)", &s, NULL);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        if (arg_all || !isempty(s))
+                                printf("%s=%s\n", name, s);
+
+                        return 0;
+
+                } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
+                        uint32_t uid;
+
+                        r = sd_bus_message_read(m, "(uo)", &uid, NULL);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        if (!uid_is_valid(uid)) {
+                                log_error("Invalid user ID: " UID_FMT, uid);
+                                return -EINVAL;
+                        }
+
+                        printf("%s=" UID_FMT "\n", name, uid);
+
+                        return 0;
+                }
+
+                break;
+
+        case SD_BUS_TYPE_ARRAY:
+
+                if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
+                        const char *s;
+                        bool space = false;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        printf("%s=", name);
+
+                        while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
+                                printf("%s%s", space ? " " : "", s);
+                                space = true;
+                        }
+
+                        printf("\n");
+
+                        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);
+
+                        return 0;
+                }
+
+                break;
+        }
+
+        r = bus_print_property(name, m, arg_all);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        if (r == 0) {
+                r = sd_bus_message_skip(m, contents);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (arg_all)
+                        printf("%s=[unprintable]\n", name);
+        }
+
+        return 0;
 }
 
 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
 
+        assert(bus);
+        assert(path);
+        assert(new_line);
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.login1",
+                        path,
+                        "org.freedesktop.DBus.Properties",
+                        "GetAll",
+                        &error,
+                        &reply,
+                        "s", "");
+        if (r < 0)
+                return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
         if (*new_line)
                 printf("\n");
 
         *new_line = true;
 
-        r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
+        while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+                const char *name, *contents;
+
+                r = sd_bus_message_read(reply, "s", &name);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_peek_type(reply, NULL, &contents);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = print_property(name, reply, contents);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
         if (r < 0)
-                log_error_errno(r, "Could not get properties: %m");
+                return bus_log_parse_error(r);
 
-        return r;
+        return 0;
 }
 
 static int show_session(int argc, char *argv[], void *userdata) {
@@ -1243,9 +1418,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return 0;
 
                 case ARG_VERSION:
-                        puts(PACKAGE_STRING);
-                        puts(SYSTEMD_FEATURES);
-                        return 0;
+                        return version();
 
                 case 'p': {
                         r = strv_extend(&arg_property, optarg);
@@ -1360,7 +1533,7 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
 }
 
 int main(int argc, char *argv[]) {
-        _cleanup_bus_close_unref_ sd_bus *bus = NULL;
+        _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
         int r;
 
         setlocale(LC_ALL, "");
@@ -1371,7 +1544,7 @@ int main(int argc, char *argv[]) {
         if (r <= 0)
                 goto finish;
 
-        r = bus_open_transport(arg_transport, arg_host, false, &bus);
+        r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0) {
                 log_error_errno(r, "Failed to create bus connection: %m");
                 goto finish;