X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogind-user.c;h=613a5c35ffaa1117c1f9901d9b49dfefb586cf59;hp=e0a38425461347a5df49ac9a69b213cc5a60fe43;hb=4c12626c8e3491570b395d68380543e10c98ad33;hpb=90821c935e5f4258dc21fc515cf721beb0914a85 diff --git a/src/logind-user.c b/src/logind-user.c index e0a384254..613a5c35f 100644 --- a/src/logind-user.c +++ b/src/logind-user.c @@ -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",