]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user.c
logind: Add a two new variables to the user session tracking file.
[thirdparty/systemd.git] / src / login / logind-user.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25
26 #include "logind-user.h"
27 #include "util.h"
28 #include "mkdir.h"
29 #include "cgroup-util.h"
30 #include "hashmap.h"
31 #include "strv.h"
32
33 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
34 User *u;
35
36 assert(m);
37 assert(name);
38
39 u = new0(User, 1);
40 if (!u)
41 return NULL;
42
43 u->name = strdup(name);
44 if (!u->name) {
45 free(u);
46 return NULL;
47 }
48
49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
50 free(u->name);
51 free(u);
52 return NULL;
53 }
54
55 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
56 free(u->state_file);
57 free(u->name);
58 free(u);
59 return NULL;
60 }
61
62 u->manager = m;
63 u->uid = uid;
64 u->gid = gid;
65
66 return u;
67 }
68
69 void user_free(User *u) {
70 assert(u);
71
72 if (u->in_gc_queue)
73 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
74
75 while (u->sessions)
76 session_free(u->sessions);
77
78 if (u->cgroup_path)
79 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
80 free(u->cgroup_path);
81
82 free(u->service);
83 free(u->runtime_path);
84
85 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
86
87 free(u->name);
88 free(u->state_file);
89 free(u);
90 }
91
92 int user_save(User *u) {
93 FILE *f;
94 int r;
95 char *temp_path;
96
97 assert(u);
98 assert(u->state_file);
99
100 if (!u->started)
101 return 0;
102
103 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
104 if (r < 0)
105 goto finish;
106
107 r = fopen_temporary(u->state_file, &f, &temp_path);
108 if (r < 0)
109 goto finish;
110
111 fchmod(fileno(f), 0644);
112
113 fprintf(f,
114 "# This is private data. Do not parse.\n"
115 "NAME=%s\n"
116 "STATE=%s\n",
117 u->name,
118 user_state_to_string(user_get_state(u)));
119
120 if (u->cgroup_path)
121 fprintf(f,
122 "CGROUP=%s\n",
123 u->cgroup_path);
124
125 if (u->runtime_path)
126 fprintf(f,
127 "RUNTIME=%s\n",
128 u->runtime_path);
129
130 if (u->service)
131 fprintf(f,
132 "SERVICE=%s\n",
133 u->service);
134
135 if (u->display)
136 fprintf(f,
137 "DISPLAY=%s\n",
138 u->display->id);
139
140 if (u->sessions) {
141 Session *i;
142 bool first;
143
144 fputs("SESSIONS=", f);
145 first = true;
146 LIST_FOREACH(sessions_by_user, i, u->sessions) {
147 if (first)
148 first = false;
149 else
150 fputc(' ', f);
151
152 fputs(i->id, f);
153 }
154
155 fputs("\nSEATS=", f);
156 first = true;
157 LIST_FOREACH(sessions_by_user, i, u->sessions) {
158 if (!i->seat)
159 continue;
160
161 if (first)
162 first = false;
163 else
164 fputc(' ', f);
165
166 fputs(i->seat->id, f);
167 }
168
169 fputs("\nACTIVE_SESSIONS=", f);
170 first = true;
171 LIST_FOREACH(sessions_by_user, i, u->sessions) {
172 if (!session_is_active(i))
173 continue;
174
175 if (first)
176 first = false;
177 else
178 fputc(' ', f);
179
180 fputs(i->id, f);
181 }
182
183 fputs("\nONLINE_SESSIONS=", f);
184 first = true;
185 LIST_FOREACH(sessions_by_user, i, u->sessions) {
186 if (session_get_state(i) == SESSION_CLOSING)
187 continue;
188
189 if (first)
190 first = false;
191 else
192 fputc(' ', f);
193
194 fputs(i->id, f);
195 }
196
197 fputs("\nACTIVE_SEATS=", f);
198 first = true;
199 LIST_FOREACH(sessions_by_user, i, u->sessions) {
200 if (!session_is_active(i) || !i->seat)
201 continue;
202
203 if (first)
204 first = false;
205 else
206 fputc(' ', f);
207
208 fputs(i->seat->id, f);
209 }
210
211 fputs("\nONLINE_SEATS=", f);
212 first = true;
213 LIST_FOREACH(sessions_by_user, i, u->sessions) {
214 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
215 continue;
216
217 if (first)
218 first = false;
219 else
220 fputc(' ', f);
221
222 fputs(i->seat->id, f);
223 }
224 fputc('\n', f);
225 }
226
227 fflush(f);
228
229 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
230 r = -errno;
231 unlink(u->state_file);
232 unlink(temp_path);
233 }
234
235 fclose(f);
236 free(temp_path);
237
238 finish:
239 if (r < 0)
240 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
241
242 return r;
243 }
244
245 int user_load(User *u) {
246 int r;
247 char *display = NULL;
248 Session *s = NULL;
249
250 assert(u);
251
252 r = parse_env_file(u->state_file, NEWLINE,
253 "CGROUP", &u->cgroup_path,
254 "RUNTIME", &u->runtime_path,
255 "SERVICE", &u->service,
256 "DISPLAY", &display,
257 NULL);
258 if (r < 0) {
259 free(display);
260
261 if (r == -ENOENT)
262 return 0;
263
264 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
265 return r;
266 }
267
268 if (display) {
269 s = hashmap_get(u->manager->sessions, display);
270 free(display);
271 }
272
273 if (s && s->display && display_is_local(s->display))
274 u->display = s;
275
276 return r;
277 }
278
279 static int user_mkdir_runtime_path(User *u) {
280 char *p;
281 int r;
282
283 assert(u);
284
285 r = mkdir_safe_label("/run/user", 0755, 0, 0);
286 if (r < 0) {
287 log_error("Failed to create /run/user: %s", strerror(-r));
288 return r;
289 }
290
291 if (!u->runtime_path) {
292 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
293 return log_oom();
294 } else
295 p = u->runtime_path;
296
297 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
298 if (r < 0) {
299 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
300 free(p);
301 u->runtime_path = NULL;
302 return r;
303 }
304
305 u->runtime_path = p;
306 return 0;
307 }
308
309 static int user_create_cgroup(User *u) {
310 char **k;
311 char *p;
312 int r;
313
314 assert(u);
315
316 if (!u->cgroup_path) {
317 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
318 return log_oom();
319 } else
320 p = u->cgroup_path;
321
322 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
323 if (r < 0) {
324 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
325 free(p);
326 u->cgroup_path = NULL;
327 return r;
328 }
329
330 u->cgroup_path = p;
331
332 STRV_FOREACH(k, u->manager->controllers) {
333
334 if (strv_contains(u->manager->reset_controllers, *k))
335 continue;
336
337 r = cg_create(*k, p);
338 if (r < 0)
339 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
340 }
341
342 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
343
344 return 0;
345 }
346
347 static int user_start_service(User *u) {
348 assert(u);
349
350 /* FIXME: Fill me in later ... */
351
352 return 0;
353 }
354
355 int user_start(User *u) {
356 int r;
357
358 assert(u);
359
360 if (u->started)
361 return 0;
362
363 log_debug("New user %s logged in.", u->name);
364
365 /* Make XDG_RUNTIME_DIR */
366 r = user_mkdir_runtime_path(u);
367 if (r < 0)
368 return r;
369
370 /* Create cgroup */
371 r = user_create_cgroup(u);
372 if (r < 0)
373 return r;
374
375 /* Spawn user systemd */
376 r = user_start_service(u);
377 if (r < 0)
378 return r;
379
380 dual_timestamp_get(&u->timestamp);
381
382 u->started = true;
383
384 /* Save new user data */
385 user_save(u);
386
387 user_send_signal(u, true);
388
389 return 0;
390 }
391
392 static int user_stop_service(User *u) {
393 assert(u);
394
395 if (!u->service)
396 return 0;
397
398 return 0;
399 }
400
401 static int user_shall_kill(User *u) {
402 assert(u);
403
404 if (!u->manager->kill_user_processes)
405 return false;
406
407 if (strv_contains(u->manager->kill_exclude_users, u->name))
408 return false;
409
410 if (strv_isempty(u->manager->kill_only_users))
411 return true;
412
413 return strv_contains(u->manager->kill_only_users, u->name);
414 }
415
416 static int user_terminate_cgroup(User *u) {
417 int r;
418 char **k;
419
420 assert(u);
421
422 if (!u->cgroup_path)
423 return 0;
424
425 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
426
427 if (user_shall_kill(u)) {
428
429 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
430 if (r < 0)
431 log_error("Failed to kill user cgroup: %s", strerror(-r));
432 } else {
433
434 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
435 if (r < 0)
436 log_error("Failed to check user cgroup: %s", strerror(-r));
437 else if (r > 0) {
438 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
439 if (r < 0)
440 log_error("Failed to delete user cgroup: %s", strerror(-r));
441 } else
442 r = -EBUSY;
443 }
444
445 STRV_FOREACH(k, u->manager->controllers)
446 cg_trim(*k, u->cgroup_path, true);
447
448 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
449
450 free(u->cgroup_path);
451 u->cgroup_path = NULL;
452
453 return r;
454 }
455
456 static int user_remove_runtime_path(User *u) {
457 int r;
458
459 assert(u);
460
461 if (!u->runtime_path)
462 return 0;
463
464 r = rm_rf(u->runtime_path, false, true, false);
465 if (r < 0)
466 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
467
468 free(u->runtime_path);
469 u->runtime_path = NULL;
470
471 return r;
472 }
473
474 int user_stop(User *u) {
475 Session *s;
476 int r = 0, k;
477 assert(u);
478
479 if (u->started)
480 log_debug("User %s logged out.", u->name);
481
482 LIST_FOREACH(sessions_by_user, s, u->sessions) {
483 k = session_stop(s);
484 if (k < 0)
485 r = k;
486 }
487
488 /* Kill systemd */
489 k = user_stop_service(u);
490 if (k < 0)
491 r = k;
492
493 /* Kill cgroup */
494 k = user_terminate_cgroup(u);
495 if (k < 0)
496 r = k;
497
498 /* Kill XDG_RUNTIME_DIR */
499 k = user_remove_runtime_path(u);
500 if (k < 0)
501 r = k;
502
503 unlink(u->state_file);
504 user_add_to_gc_queue(u);
505
506 if (u->started)
507 user_send_signal(u, false);
508
509 u->started = false;
510
511 return r;
512 }
513
514 int user_get_idle_hint(User *u, dual_timestamp *t) {
515 Session *s;
516 bool idle_hint = true;
517 dual_timestamp ts = { 0, 0 };
518
519 assert(u);
520
521 LIST_FOREACH(sessions_by_user, s, u->sessions) {
522 dual_timestamp k;
523 int ih;
524
525 ih = session_get_idle_hint(s, &k);
526 if (ih < 0)
527 return ih;
528
529 if (!ih) {
530 if (!idle_hint) {
531 if (k.monotonic < ts.monotonic)
532 ts = k;
533 } else {
534 idle_hint = false;
535 ts = k;
536 }
537 } else if (idle_hint) {
538
539 if (k.monotonic > ts.monotonic)
540 ts = k;
541 }
542 }
543
544 if (t)
545 *t = ts;
546
547 return idle_hint;
548 }
549
550 static int user_check_linger_file(User *u) {
551 char *p;
552 int r;
553
554 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
555 return -ENOMEM;
556
557 r = access(p, F_OK) >= 0;
558 free(p);
559
560 return r;
561 }
562
563 int user_check_gc(User *u, bool drop_not_started) {
564 int r;
565
566 assert(u);
567
568 if (drop_not_started && !u->started)
569 return 0;
570
571 if (u->sessions)
572 return 1;
573
574 if (user_check_linger_file(u) > 0)
575 return 1;
576
577 if (u->cgroup_path) {
578 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
579 if (r < 0)
580 return r;
581
582 if (r <= 0)
583 return 1;
584 }
585
586 return 0;
587 }
588
589 void user_add_to_gc_queue(User *u) {
590 assert(u);
591
592 if (u->in_gc_queue)
593 return;
594
595 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
596 u->in_gc_queue = true;
597 }
598
599 UserState user_get_state(User *u) {
600 Session *i;
601 bool all_closing = true;
602
603 assert(u);
604
605
606 LIST_FOREACH(sessions_by_user, i, u->sessions) {
607 if (session_is_active(i))
608 return USER_ACTIVE;
609 if (session_get_state(i) != SESSION_CLOSING)
610 all_closing = false;
611 }
612
613 if (u->sessions)
614 return all_closing ? USER_CLOSING : USER_ONLINE;
615
616 if (user_check_linger_file(u) > 0)
617 return USER_LINGERING;
618
619 return USER_CLOSING;
620 }
621
622 int user_kill(User *u, int signo) {
623 int r = 0, q;
624 Set *pid_set = NULL;
625
626 assert(u);
627
628 if (!u->cgroup_path)
629 return -ESRCH;
630
631 pid_set = set_new(trivial_hash_func, trivial_compare_func);
632 if (!pid_set)
633 return -ENOMEM;
634
635 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
636 if (q < 0)
637 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
638 r = q;
639
640 if (pid_set)
641 set_free(pid_set);
642
643 return r;
644 }
645
646 static const char* const user_state_table[_USER_STATE_MAX] = {
647 [USER_OFFLINE] = "offline",
648 [USER_LINGERING] = "lingering",
649 [USER_ONLINE] = "online",
650 [USER_ACTIVE] = "active",
651 [USER_CLOSING] = "closing"
652 };
653
654 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);