chiark / gitweb /
umask: change default umask to 0022 just to be sure, and set it explicitly in all...
[elogind.git] / src / logind-user.c
index e0a38425461347a5df49ac9a69b213cc5a60fe43..613a5c35ffaa1117c1f9901d9b49dfefb586cf59 100644 (file)
@@ -35,7 +35,7 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
         assert(m);
         assert(name);
 
-        u = new(User, 1);
+        u = new0(User, 1);
         if (!u)
                 return NULL;
 
@@ -45,7 +45,7 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
                 return NULL;
         }
 
-        if (asprintf(&u->state_file, "/run/systemd/user/%lu", (unsigned long) uid) < 0) {
+        if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
                 free(u->name);
                 free(u);
                 return NULL;
@@ -68,6 +68,9 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
 void user_free(User *u) {
         assert(u);
 
+        if (u->in_gc_queue)
+                LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
+
         while (u->sessions)
                 session_free(u->sessions);
 
@@ -86,19 +89,26 @@ void user_free(User *u) {
 int user_save(User *u) {
         FILE *f;
         int r;
+        char *temp_path;
 
         assert(u);
         assert(u->state_file);
 
-        r = safe_mkdir("/run/systemd/user", 0755, 0, 0);
+        if (!u->started)
+                return 0;
+
+        r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
         if (r < 0)
-                return r;
+                goto finish;
 
-        f = fopen(u->state_file, "we");
-        if (!f)
-                return -errno;
+        r = fopen_temporary(u->state_file, &f, &temp_path);
+        if (r < 0)
+                goto finish;
+
+        fchmod(fileno(f), 0644);
 
         fprintf(f,
+                "# This is private data. Do not parse.\n"
                 "NAME=%s\n"
                 "STATE=%s\n",
                 u->name,
@@ -124,24 +134,70 @@ int user_save(User *u) {
                         "DISPLAY=%s\n",
                         u->display->id);
 
+        if (u->sessions) {
+                Session *i;
+
+                fputs("SESSIONS=", f);
+                LIST_FOREACH(sessions_by_user, i, u->sessions) {
+                        fprintf(f,
+                                "%s%c",
+                                i->id,
+                                i->sessions_by_user_next ? ' ' : '\n');
+                }
+
+                fputs("SEATS=", f);
+                LIST_FOREACH(sessions_by_user, i, u->sessions) {
+                        if (i->seat)
+                                fprintf(f,
+                                        "%s%c",
+                                        i->seat->id,
+                                        i->sessions_by_user_next ? ' ' : '\n');
+                }
+
+                fputs("ACTIVE_SESSIONS=", f);
+                LIST_FOREACH(sessions_by_user, i, u->sessions)
+                        if (session_is_active(i))
+                                fprintf(f,
+                                        "%lu%c",
+                                        (unsigned long) i->user->uid,
+                                        i->sessions_by_user_next ? ' ' : '\n');
+
+                fputs("ACTIVE_SEATS=", f);
+                LIST_FOREACH(sessions_by_user, i, u->sessions) {
+                        if (session_is_active(i) && i->seat)
+                                fprintf(f,
+                                        "%s%c",
+                                        i->seat->id,
+                                        i->sessions_by_user_next ? ' ' : '\n');
+                }
+        }
+
         fflush(f);
-        if (ferror(f)) {
+
+        if (ferror(f) || rename(temp_path, u->state_file) < 0) {
                 r = -errno;
                 unlink(u->state_file);
+                unlink(temp_path);
         }
 
         fclose(f);
+        free(temp_path);
+
+finish:
+        if (r < 0)
+                log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
+
         return r;
 }
 
 int user_load(User *u) {
         int r;
         char *display = NULL;
-        Session *s;
+        Session *s = NULL;
 
         assert(u);
 
-        r = parse_env_file(u->state_file, "r",
+        r = parse_env_file(u->state_file, NEWLINE,
                            "CGROUP", &u->cgroup_path,
                            "RUNTIME", &u->runtime_path,
                            "SERVICE", &u->service,
@@ -157,10 +213,12 @@ int user_load(User *u) {
                 return r;
         }
 
-        s = hashmap_get(u->manager->sessions, display);
-        free(display);
+        if (display) {
+                s = hashmap_get(u->manager->sessions, display);
+                free(display);
+        }
 
-        if (s && s->display && x11_display_is_local(s->display))
+        if (s && s->display && display_is_local(s->display))
                 u->display = s;
 
         return r;
@@ -217,15 +275,19 @@ static int user_create_cgroup(User *u) {
 
         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
         if (r < 0) {
+                log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
                 free(p);
                 u->cgroup_path = NULL;
-                log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
                 return r;
         }
 
         u->cgroup_path = p;
 
         STRV_FOREACH(k, u->manager->controllers) {
+
+                if (strv_contains(u->manager->reset_controllers, *k))
+                        continue;
+
                 r = cg_create(*k, p);
                 if (r < 0)
                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
@@ -245,6 +307,11 @@ int user_start(User *u) {
 
         assert(u);
 
+        if (u->started)
+                return 0;
+
+        log_info("New user %s logged in.", u->name);
+
         /* Make XDG_RUNTIME_DIR */
         r = user_mkdir_runtime_path(u);
         if (r < 0)
@@ -262,6 +329,13 @@ int user_start(User *u) {
 
         dual_timestamp_get(&u->timestamp);
 
+        u->started = true;
+
+        /* Save new user data */
+        user_save(u);
+
+        user_send_signal(u, true);
+
         return 0;
 }
 
@@ -277,10 +351,19 @@ static int user_stop_service(User *u) {
 static int user_shall_kill(User *u) {
         assert(u);
 
-        return u->manager->kill_user_processes;
+        if (!u->manager->kill_user_processes)
+                return false;
+
+        if (strv_contains(u->manager->kill_exclude_users, u->name))
+                return false;
+
+        if (strv_isempty(u->manager->kill_only_users))
+                return true;
+
+        return strv_contains(u->manager->kill_only_users, u->name);
 }
 
-static int user_kill_cgroup(User *u) {
+static int user_terminate_cgroup(User *u) {
         int r;
         char **k;
 
@@ -341,6 +424,9 @@ int user_stop(User *u) {
         int r = 0, k;
         assert(u);
 
+        if (u->started)
+                log_info("User %s logged out.", u->name);
+
         LIST_FOREACH(sessions_by_user, s, u->sessions) {
                 k = session_stop(s);
                 if (k < 0)
@@ -353,7 +439,7 @@ int user_stop(User *u) {
                 r = k;
 
         /* Kill cgroup */
-        k = user_kill_cgroup(u);
+        k = user_terminate_cgroup(u);
         if (k < 0)
                 r = k;
 
@@ -362,15 +448,62 @@ int user_stop(User *u) {
         if (k < 0)
                 r = k;
 
+        unlink(u->state_file);
+        user_add_to_gc_queue(u);
+
+        if (u->started)
+                user_send_signal(u, false);
+
+        u->started = false;
+
         return r;
 }
 
-int user_check_gc(User *u) {
+int user_get_idle_hint(User *u, dual_timestamp *t) {
+        Session *s;
+        bool idle_hint = true;
+        dual_timestamp ts = { 0, 0 };
+
+        assert(u);
+
+        LIST_FOREACH(sessions_by_user, s, u->sessions) {
+                dual_timestamp k;
+                int ih;
+
+                ih = session_get_idle_hint(s, &k);
+                if (ih < 0)
+                        return ih;
+
+                if (!ih) {
+                        if (!idle_hint) {
+                                if (k.monotonic < ts.monotonic)
+                                        ts = k;
+                        } else {
+                                idle_hint = false;
+                                ts = k;
+                        }
+                } else if (idle_hint) {
+
+                        if (k.monotonic > ts.monotonic)
+                                ts = k;
+                }
+        }
+
+        if (t)
+                *t = ts;
+
+        return idle_hint;
+}
+
+int user_check_gc(User *u, bool drop_not_started) {
         int r;
         char *p;
 
         assert(u);
 
+        if (drop_not_started && !u->started)
+                return 0;
+
         if (u->sessions)
                 return 1;
 
@@ -395,6 +528,16 @@ int user_check_gc(User *u) {
         return 0;
 }
 
+void user_add_to_gc_queue(User *u) {
+        assert(u);
+
+        if (u->in_gc_queue)
+                return;
+
+        LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
+        u->in_gc_queue = true;
+}
+
 UserState user_get_state(User *u) {
         Session *i;
 
@@ -410,6 +553,30 @@ UserState user_get_state(User *u) {
         return USER_ONLINE;
 }
 
+int user_kill(User *u, int signo) {
+        int r = 0, q;
+        Set *pid_set = NULL;
+
+        assert(u);
+
+        if (!u->cgroup_path)
+                return -ESRCH;
+
+        pid_set = set_new(trivial_hash_func, trivial_compare_func);
+        if (!pid_set)
+                return -ENOMEM;
+
+        q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+        if (q < 0)
+                if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+                        r = q;
+
+        if (pid_set)
+                set_free(pid_set);
+
+        return r;
+}
+
 static const char* const user_state_table[_USER_STATE_MAX] = {
         [USER_OFFLINE] = "offline",
         [USER_LINGERING] = "lingering",