X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flogind-user.c;h=177e88200a9f49d4309a1dd621eb496faffe2aa5;hp=e0a38425461347a5df49ac9a69b213cc5a60fe43;hb=7f7bb9467931e855cdf5ec46e53c8eb46aa778f5;hpb=90821c935e5f4258dc21fc515cf721beb0914a85 diff --git a/src/logind-user.c b/src/logind-user.c index e0a384254..177e88200 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; + + r = fopen_temporary(u->state_file, &f, &temp_path); + if (r < 0) + goto finish; - f = fopen(u->state_file, "we"); - if (!f) - return -errno; + fchmod(fileno(f), 0644); fprintf(f, + "# This is private data. Do not parse.\n" "NAME=%s\n" "STATE=%s\n", u->name, @@ -125,23 +135,31 @@ int user_save(User *u) { u->display->id); 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 +175,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,9 +237,9 @@ 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; } @@ -245,6 +265,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 +287,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,7 +309,16 @@ 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) { @@ -341,6 +382,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) @@ -362,9 +406,53 @@ 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_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) { int r; char *p; @@ -395,6 +483,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;