chiark / gitweb /
util: more join() optimizations
[elogind.git] / src / sd-login.c
index 3ae850d80116ba4201e7ec9d4b83a5d6ae40bf69..2489d78c603dba842113eda63d8ee663eb0100ce 100644 (file)
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <sys/inotify.h>
 
 #include "util.h"
 #include "cgroup-util.h"
 #include "macro.h"
 #include "sd-login.h"
+#include "strv.h"
 
-_public_ int sd_pid_get_session(pid_t pid, char **session) {
-        int r;
+static int pid_get_cgroup(pid_t pid, char **root, char **cgroup) {
         char *cg_process, *cg_init, *p;
+        int r;
 
         if (pid == 0)
                 pid = getpid();
@@ -38,9 +40,6 @@ _public_ int sd_pid_get_session(pid_t pid, char **session) {
         if (pid <= 0)
                 return -EINVAL;
 
-        if (!session)
-                return -EINVAL;
-
         r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
         if (r < 0)
                 return r;
@@ -63,26 +62,57 @@ _public_ int sd_pid_get_session(pid_t pid, char **session) {
 
         free(cg_init);
 
-        if (!startswith(p, "/user/")) {
-                free(cg_process);
-                return -ENOENT;
+        if (cgroup) {
+                char* c;
+
+                c = strdup(p);
+                if (!c) {
+                        free(cg_process);
+                        return -ENOMEM;
+                }
+
+                *cgroup = c;
         }
 
-        p += 6;
-        if (startswith(p, "shared/") || streq(p, "shared")) {
+        if (root) {
+                cg_process[p-cg_process] = 0;
+                *root = cg_process;
+        } else
                 free(cg_process);
+
+        return 0;
+}
+
+_public_ int sd_pid_get_session(pid_t pid, char **session) {
+        int r;
+        char *cgroup, *p;
+
+        if (!session)
+                return -EINVAL;
+
+        r = pid_get_cgroup(pid, NULL, &cgroup);
+        if (r < 0)
+                return r;
+
+        if (!startswith(cgroup, "/user/")) {
+                free(cgroup);
                 return -ENOENT;
         }
 
-        p = strchr(p, '/');
+        p = strchr(cgroup + 6, '/');
         if (!p) {
-                free(cg_process);
+                free(cgroup);
                 return -ENOENT;
         }
 
         p++;
+        if (startswith(p, "shared/") || streq(p, "shared")) {
+                free(cgroup);
+                return -ENOENT;
+        }
+
         p = strndup(p, strcspn(p, "/"));
-        free(cg_process);
+        free(cgroup);
 
         if (!p)
                 return -ENOMEM;
@@ -91,6 +121,54 @@ _public_ int sd_pid_get_session(pid_t pid, char **session) {
         return 0;
 }
 
+_public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
+        int r;
+        char *root, *cgroup, *p, *cc;
+        struct stat st;
+
+        if (!uid)
+                return -EINVAL;
+
+        r = pid_get_cgroup(pid, &root, &cgroup);
+        if (r < 0)
+                return r;
+
+        if (!startswith(cgroup, "/user/")) {
+                free(cgroup);
+                free(root);
+                return -ENOENT;
+        }
+
+        p = strchr(cgroup + 6, '/');
+        if (!p) {
+                free(cgroup);
+                return -ENOENT;
+        }
+
+        p++;
+        p += strcspn(p, "/");
+        *p = 0;
+
+        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, cgroup, &cc);
+        free(root);
+        free(cgroup);
+
+        if (r < 0)
+                return -ENOMEM;
+
+        r = lstat(cc, &st);
+        free(cc);
+
+        if (r < 0)
+                return -errno;
+
+        if (!S_ISDIR(st.st_mode))
+                return -ENOTDIR;
+
+        *uid = st.st_uid;
+        return 0;
+}
+
 _public_ int sd_uid_get_state(uid_t uid, char**state) {
         char *p, *s = NULL;
         int r;
@@ -122,19 +200,22 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) {
         return 0;
 }
 
-static int uid_is_on_seat_internal(uid_t uid, const char *seat, const char *variable) {
+_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
         char *p, *w, *t, *state, *s = NULL;
         size_t l;
         int r;
+        const char *variable;
 
         if (!seat)
                 return -EINVAL;
 
+        variable = require_active ? "ACTIVE_UID" : "UIDS";
+
         p = strappend("/run/systemd/seats/", seat);
         if (!p)
                 return -ENOMEM;
 
-        r = parse_env_file(p, NEWLINE, "UIDS", &s, NULL);
+        r = parse_env_file(p, NEWLINE, variable, &s, NULL);
         free(p);
 
         if (r < 0) {
@@ -165,12 +246,60 @@ static int uid_is_on_seat_internal(uid_t uid, const char *seat, const char *vari
         return 0;
 }
 
-_public_ int sd_uid_is_on_seat(uid_t uid, const char *seat) {
-        return uid_is_on_seat_internal(uid, seat, "UIDS");
+static int uid_get_array(uid_t uid, const char *variable, char ***array) {
+        char *p, *s = NULL;
+        char **a;
+        int r;
+
+        if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
+                return -ENOMEM;
+
+        r = parse_env_file(p, NEWLINE,
+                           variable, &s,
+                           NULL);
+        free(p);
+
+        if (r < 0) {
+                free(s);
+
+                if (r == -ENOENT) {
+                        if (array)
+                                *array = NULL;
+                        return 0;
+                }
+
+                return r;
+        }
+
+        if (!s) {
+                if (array)
+                        *array = NULL;
+                return 0;
+        }
+
+        a = strv_split(s, " ");
+        free(s);
+
+        if (!a)
+                return -ENOMEM;
+
+        strv_uniq(a);
+        r = strv_length(a);
+
+        if (array)
+                *array = a;
+        else
+                strv_free(a);
+
+        return r;
 }
 
-_public_ int sd_uid_is_active_on_seat(uid_t uid, const char *seat) {
-        return uid_is_on_seat_internal(uid, seat, "ACTIVE_UID");
+_public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
+        return uid_get_array(uid, require_active ? "ACTIVE_SESSIONS" : "SESSIONS", sessions);
+}
+
+_public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
+        return uid_get_array(uid, require_active ? "ACTIVE_SEATS" : "SEATS", seats);
 }
 
 _public_ int sd_session_is_active(const char *session) {
@@ -204,7 +333,6 @@ _public_ int sd_session_is_active(const char *session) {
 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
         int r;
         char *p, *s = NULL;
-        unsigned long ul;
 
         if (!session)
                 return -EINVAL;
@@ -226,14 +354,10 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
         if (!s)
                 return -EIO;
 
-        r = safe_atolu(s, &ul);
+        r = parse_uid(s, uid);
         free(s);
 
-        if (r < 0)
-                return r;
-
-        *uid = (uid_t) ul;
-        return 0;
+        return r;
 }
 
 _public_ int sd_session_get_seat(const char *session, char **seat) {
@@ -291,25 +415,21 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
 
         if (session && !s)  {
                 free(t);
-                return -EIO;
+                return -ENOENT;
         }
 
         if (uid && !t) {
                 free(s);
-                return -EIO;
+                return -ENOENT;
         }
 
         if (uid && t) {
-                unsigned long ul;
-
-                r = safe_atolu(t, &ul);
+                r = parse_uid(t, uid);
                 if (r < 0) {
                         free(t);
                         free(s);
                         return r;
                 }
-
-                *uid = (uid_t) ul;
         }
 
         free(t);
@@ -321,3 +441,277 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
 
         return 0;
 }
+
+_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
+        char *p, *s = NULL, *t = NULL, **a = NULL;
+        uid_t *b = NULL;
+        unsigned n = 0;
+        int r;
+
+        if (!seat)
+                return -EINVAL;
+
+        p = strappend("/run/systemd/seats/", seat);
+        if (!p)
+                return -ENOMEM;
+
+        r = parse_env_file(p, NEWLINE,
+                           "SESSIONS", &s,
+                           "ACTIVE_SESSIONS", &t,
+                           NULL);
+        free(p);
+
+        if (r < 0) {
+                free(s);
+                free(t);
+                return r;
+        }
+
+        if (s) {
+                a = strv_split(s, " ");
+                if (!a) {
+                        free(s);
+                        free(t);
+                        return -ENOMEM;
+                }
+        }
+
+        free(s);
+
+        if (uids && t) {
+                char *w, *state;
+                size_t l;
+                unsigned i = 0;
+
+                FOREACH_WORD(w, l, t, state)
+                        n++;
+
+                b = new(uid_t, n);
+                if (!b) {
+                        strv_free(a);
+                        return -ENOMEM;
+                }
+
+                FOREACH_WORD(w, l, t, state) {
+                        char *k;
+
+                        k = strndup(w, l);
+                        if (!k) {
+                                free(t);
+                                free(b);
+                                return -ENOMEM;
+                        }
+
+                        r = parse_uid(k, b + i);
+                        free(k);
+                        if (r < 0)
+                                continue;
+
+                        i++;
+                }
+        }
+
+        free(t);
+
+        r = strv_length(a);
+
+        if (sessions)
+                *sessions = a;
+        else
+                strv_free(a);
+
+        if (uids)
+                *uids = b;
+
+        if (n_uids)
+                *n_uids = n;
+
+        return r;
+}
+
+_public_ int sd_seat_can_multi_session(const char *seat) {
+        char *p, *s = NULL;
+        int r;
+
+        if (!seat)
+                return -EINVAL;
+
+        p = strappend("/run/systemd/seats/", seat);
+        if (!p)
+                return -ENOMEM;
+
+        r = parse_env_file(p, NEWLINE,
+                           "IS_VTCONSOLE", &s,
+                           NULL);
+        free(p);
+
+        if (r < 0) {
+                free(s);
+                return r;
+        }
+
+        if (s) {
+                r = parse_boolean(s);
+                free(s);
+        } else
+                r = 0;
+
+        return r;
+}
+
+_public_ int sd_get_seats(char ***seats) {
+        return get_files_in_directory("/run/systemd/seats/", seats);
+}
+
+_public_ int sd_get_sessions(char ***sessions) {
+        return get_files_in_directory("/run/systemd/sessions/", sessions);
+}
+
+_public_ int sd_get_uids(uid_t **users) {
+        DIR *d;
+        int r = 0;
+        unsigned n = 0;
+        uid_t *l = NULL;
+
+        d = opendir("/run/systemd/users/");
+        for (;;) {
+                struct dirent buffer, *de;
+                int k;
+                uid_t uid;
+
+                k = readdir_r(d, &buffer, &de);
+                if (k != 0) {
+                        r = -k;
+                        goto finish;
+                }
+
+                if (!de)
+                        break;
+
+                dirent_ensure_type(d, de);
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                k = parse_uid(de->d_name, &uid);
+                if (k < 0)
+                        continue;
+
+                if (users) {
+                        if ((unsigned) r >= n) {
+                                uid_t *t;
+
+                                n = MAX(16, 2*r);
+                                t = realloc(l, sizeof(uid_t) * n);
+                                if (!t) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                l = t;
+                        }
+
+                        assert((unsigned) r < n);
+                        l[r++] = uid;
+                } else
+                        r++;
+        }
+
+finish:
+        if (d)
+                closedir(d);
+
+        if (r >= 0) {
+                if (users)
+                        *users = l;
+        } else
+                free(l);
+
+        return r;
+}
+
+static inline int MONITOR_TO_FD(sd_login_monitor *m) {
+        return (int) (unsigned long) m - 1;
+}
+
+static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
+        return (sd_login_monitor*) (unsigned long) (fd + 1);
+}
+
+_public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
+        int fd, k;
+        bool good = false;
+
+        if (!m)
+                return -EINVAL;
+
+        fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+        if (fd < 0)
+                return errno;
+
+        if (!category || streq(category, "seat")) {
+                k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
+                if (k < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                good = true;
+        }
+
+        if (!category || streq(category, "session")) {
+                k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
+                if (k < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                good = true;
+        }
+
+        if (!category || streq(category, "uid")) {
+                k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
+                if (k < 0) {
+                        close_nointr_nofail(fd);
+                        return -errno;
+                }
+
+                good = true;
+        }
+
+        if (!good) {
+                close_nointr(fd);
+                return -EINVAL;
+        }
+
+        *m = FD_TO_MONITOR(fd);
+        return 0;
+}
+
+_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
+        int fd;
+
+        if (!m)
+                return NULL;
+
+        fd = MONITOR_TO_FD(m);
+        close_nointr(fd);
+
+        return NULL;
+}
+
+_public_ int sd_login_monitor_flush(sd_login_monitor *m) {
+
+        if (!m)
+                return -EINVAL;
+
+        return flush_fd(MONITOR_TO_FD(m));
+}
+
+_public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
+
+        if (!m)
+                return -EINVAL;
+
+        return MONITOR_TO_FD(m);
+}