]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
util: rework rm_rf() logic
[thirdparty/systemd.git] / src / login / logind-user.c
CommitLineData
20263082
LP
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
5430f7f2
LP
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
20263082
LP
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
5430f7f2 16 Lesser General Public License for more details.
20263082 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
20263082
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1c231f56 22#include <sys/mount.h>
20263082
LP
23#include <string.h>
24#include <unistd.h>
25#include <errno.h>
26
20263082 27#include "util.h"
49e942b2 28#include "mkdir.h"
c6878637 29#include "rm-rf.h"
20263082 30#include "hashmap.h"
a5c32cff 31#include "fileio.h"
1c231f56 32#include "path-util.h"
9444b1f2 33#include "special.h"
fb6becb4 34#include "unit-name.h"
cc377381
LP
35#include "bus-util.h"
36#include "bus-error.h"
1c231f56 37#include "conf-parser.h"
66cdd0f2 38#include "clean-ipc.h"
fb6becb4 39#include "logind-user.h"
374738d5 40#include "smack-util.h"
20263082
LP
41
42User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
43 User *u;
44
45 assert(m);
46 assert(name);
47
14c3baca 48 u = new0(User, 1);
20263082
LP
49 if (!u)
50 return NULL;
51
52 u->name = strdup(name);
9444b1f2
LP
53 if (!u->name)
54 goto fail;
20263082 55
de0671ee 56 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
9444b1f2 57 goto fail;
20263082 58
8cb4ab00 59 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
9444b1f2 60 goto fail;
20263082
LP
61
62 u->manager = m;
63 u->uid = uid;
64 u->gid = gid;
65
66 return u;
9444b1f2
LP
67
68fail:
69 free(u->state_file);
70 free(u->name);
71 free(u);
72
73 return NULL;
20263082
LP
74}
75
76void user_free(User *u) {
77 assert(u);
78
14c3baca 79 if (u->in_gc_queue)
71fda00f 80 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
14c3baca 81
20263082
LP
82 while (u->sessions)
83 session_free(u->sessions);
84
fb6becb4
LP
85 if (u->slice) {
86 hashmap_remove(u->manager->user_units, u->slice);
87 free(u->slice);
9444b1f2 88 }
20263082 89
fb6becb4
LP
90 if (u->service) {
91 hashmap_remove(u->manager->user_units, u->service);
92 free(u->service);
93 }
94
95 free(u->slice_job);
96 free(u->service_job);
97
20263082
LP
98 free(u->runtime_path);
99
8cb4ab00 100 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
20263082
LP
101
102 free(u->name);
d2f92cdf 103 free(u->state_file);
20263082
LP
104 free(u);
105}
106
107int user_save(User *u) {
9444b1f2
LP
108 _cleanup_free_ char *temp_path = NULL;
109 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
110 int r;
111
112 assert(u);
113 assert(u->state_file);
114
accaeded
LP
115 if (!u->started)
116 return 0;
117
d2e54fae 118 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 119 if (r < 0)
14c3baca 120 goto finish;
20263082 121
14c3baca
LP
122 r = fopen_temporary(u->state_file, &f, &temp_path);
123 if (r < 0)
124 goto finish;
125
126 fchmod(fileno(f), 0644);
20263082
LP
127
128 fprintf(f,
14c3baca 129 "# This is private data. Do not parse.\n"
20263082
LP
130 "NAME=%s\n"
131 "STATE=%s\n",
132 u->name,
133 user_state_to_string(user_get_state(u)));
134
20263082 135 if (u->runtime_path)
9444b1f2 136 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
20263082
LP
137
138 if (u->service)
9444b1f2 139 fprintf(f, "SERVICE=%s\n", u->service);
fb6becb4
LP
140 if (u->service_job)
141 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
9444b1f2
LP
142
143 if (u->slice)
144 fprintf(f, "SLICE=%s\n", u->slice);
fb6becb4
LP
145 if (u->slice_job)
146 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
20263082
LP
147
148 if (u->display)
9444b1f2
LP
149 fprintf(f, "DISPLAY=%s\n", u->display->id);
150
151 if (dual_timestamp_is_set(&u->timestamp))
20263082 152 fprintf(f,
90b2de37
ZJS
153 "REALTIME="USEC_FMT"\n"
154 "MONOTONIC="USEC_FMT"\n",
155 u->timestamp.realtime,
156 u->timestamp.monotonic);
20263082 157
034a2a52
LP
158 if (u->sessions) {
159 Session *i;
9b958eff 160 bool first;
034a2a52
LP
161
162 fputs("SESSIONS=", f);
9b958eff 163 first = true;
034a2a52 164 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
165 if (first)
166 first = false;
167 else
168 fputc(' ', f);
169
170 fputs(i->id, f);
034a2a52
LP
171 }
172
9b958eff
LP
173 fputs("\nSEATS=", f);
174 first = true;
034a2a52 175 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
176 if (!i->seat)
177 continue;
178
179 if (first)
180 first = false;
181 else
182 fputc(' ', f);
183
184 fputs(i->seat->id, f);
034a2a52
LP
185 }
186
9b958eff
LP
187 fputs("\nACTIVE_SESSIONS=", f);
188 first = true;
189 LIST_FOREACH(sessions_by_user, i, u->sessions) {
190 if (!session_is_active(i))
191 continue;
192
193 if (first)
194 first = false;
195 else
196 fputc(' ', f);
197
198 fputs(i->id, f);
199 }
034a2a52 200
2dc8f41a
CG
201 fputs("\nONLINE_SESSIONS=", f);
202 first = true;
203 LIST_FOREACH(sessions_by_user, i, u->sessions) {
204 if (session_get_state(i) == SESSION_CLOSING)
205 continue;
206
207 if (first)
208 first = false;
209 else
210 fputc(' ', f);
211
212 fputs(i->id, f);
213 }
214
9b958eff
LP
215 fputs("\nACTIVE_SEATS=", f);
216 first = true;
034a2a52 217 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
218 if (!session_is_active(i) || !i->seat)
219 continue;
220
221 if (first)
222 first = false;
223 else
47acb2f1
CG
224 fputc(' ', f);
225
226 fputs(i->seat->id, f);
034a2a52 227 }
2dc8f41a
CG
228
229 fputs("\nONLINE_SEATS=", f);
230 first = true;
231 LIST_FOREACH(sessions_by_user, i, u->sessions) {
232 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
233 continue;
234
235 if (first)
236 first = false;
237 else
238 fputc(' ', f);
239
240 fputs(i->seat->id, f);
241 }
9b958eff 242 fputc('\n', f);
034a2a52
LP
243 }
244
20263082 245 fflush(f);
14c3baca
LP
246
247 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
20263082
LP
248 r = -errno;
249 unlink(u->state_file);
14c3baca 250 unlink(temp_path);
20263082
LP
251 }
252
14c3baca
LP
253finish:
254 if (r < 0)
da927ba9 255 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
14c3baca 256
20263082
LP
257 return r;
258}
259
260int user_load(User *u) {
9444b1f2 261 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
98a28fef 262 Session *s = NULL;
9444b1f2 263 int r;
20263082
LP
264
265 assert(u);
266
a185c5aa 267 r = parse_env_file(u->state_file, NEWLINE,
fb6becb4
LP
268 "RUNTIME", &u->runtime_path,
269 "SERVICE", &u->service,
270 "SERVICE_JOB", &u->service_job,
271 "SLICE", &u->slice,
272 "SLICE_JOB", &u->slice_job,
273 "DISPLAY", &display,
274 "REALTIME", &realtime,
275 "MONOTONIC", &monotonic,
20263082
LP
276 NULL);
277 if (r < 0) {
20263082
LP
278 if (r == -ENOENT)
279 return 0;
280
da927ba9 281 log_error_errno(r, "Failed to read %s: %m", u->state_file);
20263082
LP
282 return r;
283 }
284
9444b1f2 285 if (display)
98a28fef 286 s = hashmap_get(u->manager->sessions, display);
20263082 287
4d6d6518 288 if (s && s->display && display_is_local(s->display))
20263082
LP
289 u->display = s;
290
9444b1f2
LP
291 if (realtime) {
292 unsigned long long l;
293 if (sscanf(realtime, "%llu", &l) > 0)
294 u->timestamp.realtime = l;
295 }
296
297 if (monotonic) {
298 unsigned long long l;
299 if (sscanf(monotonic, "%llu", &l) > 0)
300 u->timestamp.monotonic = l;
301 }
302
20263082
LP
303 return r;
304}
305
306static int user_mkdir_runtime_path(User *u) {
307 char *p;
308 int r;
309
310 assert(u);
311
d2e54fae 312 r = mkdir_safe_label("/run/user", 0755, 0, 0);
f647962d
MS
313 if (r < 0)
314 return log_error_errno(r, "Failed to create /run/user: %m");
20263082
LP
315
316 if (!u->runtime_path) {
1c231f56 317 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
0d0f0c50 318 return log_oom();
20263082
LP
319 } else
320 p = u->runtime_path;
321
1c231f56
LP
322 if (path_is_mount_point(p, false) <= 0) {
323 _cleanup_free_ char *t = NULL;
324
a9c67ea2 325 (void) mkdir(p, 0700);
1c231f56 326
6baa7db0 327 if (mac_smack_use())
374738d5
LS
328 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
329 else
330 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
374738d5 331 if (r < 0) {
1c231f56
LP
332 r = log_oom();
333 goto fail;
334 }
335
336 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
337 if (r < 0) {
11c6476a
CS
338 if (errno != EPERM) {
339 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
340 goto fail;
341 }
342
343 /* Lacking permissions, maybe
344 * CAP_SYS_ADMIN-less container? In this case,
345 * just use a normal directory. */
346
347 r = chmod_and_chown(p, 0700, u->uid, u->gid);
348 if (r < 0) {
349 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
350 goto fail;
351 }
1c231f56 352 }
20263082
LP
353 }
354
355 u->runtime_path = p;
356 return 0;
1c231f56
LP
357
358fail:
4d858e7d
CS
359 if (p) {
360 /* Try to clean up, but ignore errors */
361 (void) rmdir(p);
362 free(p);
363 }
364
1c231f56
LP
365 u->runtime_path = NULL;
366 return r;
20263082
LP
367}
368
fb6becb4 369static int user_start_slice(User *u) {
fb6becb4 370 char *job;
20263082
LP
371 int r;
372
373 assert(u);
374
fb6becb4 375 if (!u->slice) {
cc377381 376 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de0671ee
ZJS
377 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
378 sprintf(lu, UID_FMT, u->uid);
ae018d9b 379
d0af76e6 380 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
9444b1f2
LP
381 if (r < 0)
382 return r;
383
d0af76e6
LP
384 r = manager_start_unit(u->manager, slice, &error, &job);
385 if (r < 0) {
cc377381 386 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
d0af76e6
LP
387 free(slice);
388 } else {
389 u->slice = slice;
390
391 free(u->slice_job);
392 u->slice_job = job;
393 }
20263082
LP
394 }
395
d0af76e6
LP
396 if (u->slice)
397 hashmap_put(u->manager->user_units, u->slice, u);
398
20263082
LP
399 return 0;
400}
401
402static int user_start_service(User *u) {
cc377381 403 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
404 char *job;
405 int r;
406
20263082
LP
407 assert(u);
408
fb6becb4 409 if (!u->service) {
de0671ee
ZJS
410 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
411 sprintf(lu, UID_FMT, u->uid);
fb6becb4 412
d0af76e6
LP
413 service = unit_name_build("user", lu, ".service");
414 if (!service)
fb6becb4
LP
415 return log_oom();
416
d0af76e6
LP
417 r = manager_start_unit(u->manager, service, &error, &job);
418 if (r < 0) {
cc377381 419 log_error("Failed to start user service: %s", bus_error_message(&error, r));
d0af76e6
LP
420 free(service);
421 } else {
422 u->service = service;
423
424 free(u->service_job);
425 u->service_job = job;
426 }
fb6becb4
LP
427 }
428
d0af76e6
LP
429 if (u->service)
430 hashmap_put(u->manager->user_units, u->service, u);
431
20263082
LP
432 return 0;
433}
434
435int user_start(User *u) {
436 int r;
437
438 assert(u);
439
9418f147
LP
440 if (u->started)
441 return 0;
442
1637a8be 443 log_debug("New user %s logged in.", u->name);
ed18b08b 444
20263082
LP
445 /* Make XDG_RUNTIME_DIR */
446 r = user_mkdir_runtime_path(u);
447 if (r < 0)
448 return r;
449
450 /* Create cgroup */
fb6becb4 451 r = user_start_slice(u);
20263082
LP
452 if (r < 0)
453 return r;
454
455 /* Spawn user systemd */
456 r = user_start_service(u);
457 if (r < 0)
458 return r;
459
9444b1f2
LP
460 if (!dual_timestamp_is_set(&u->timestamp))
461 dual_timestamp_get(&u->timestamp);
20263082 462
9418f147
LP
463 u->started = true;
464
7f7bb946
LP
465 /* Save new user data */
466 user_save(u);
467
da119395
LP
468 user_send_signal(u, true);
469
20263082
LP
470 return 0;
471}
472
fb6becb4 473static int user_stop_slice(User *u) {
cc377381 474 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
475 char *job;
476 int r;
20263082 477
20263082
LP
478 assert(u);
479
fb6becb4
LP
480 if (!u->slice)
481 return 0;
482
483 r = manager_stop_unit(u->manager, u->slice, &error, &job);
484 if (r < 0) {
cc377381 485 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
486 return r;
487 }
ed18b08b 488
fb6becb4
LP
489 free(u->slice_job);
490 u->slice_job = job;
ed18b08b 491
fb6becb4 492 return r;
20263082
LP
493}
494
fb6becb4 495static int user_stop_service(User *u) {
cc377381 496 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 497 char *job;
20263082 498 int r;
20263082
LP
499
500 assert(u);
501
fb6becb4 502 if (!u->service)
20263082
LP
503 return 0;
504
fb6becb4
LP
505 r = manager_stop_unit(u->manager, u->service, &error, &job);
506 if (r < 0) {
cc377381 507 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
fb6becb4
LP
508 return r;
509 }
20263082 510
fb6becb4
LP
511 free(u->service_job);
512 u->service_job = job;
20263082 513
fb6becb4
LP
514 return r;
515}
20263082 516
20263082
LP
517static int user_remove_runtime_path(User *u) {
518 int r;
519
520 assert(u);
521
522 if (!u->runtime_path)
523 return 0;
524
c6878637 525 r = rm_rf(u->runtime_path, 0);
1c231f56 526 if (r < 0)
da927ba9 527 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
1c231f56 528
11c6476a
CS
529 /* Ignore cases where the directory isn't mounted, as that's
530 * quite possible, if we lacked the permissions to mount
531 * something */
532 r = umount2(u->runtime_path, MNT_DETACH);
533 if (r < 0 && errno != EINVAL && errno != ENOENT)
56f64d95 534 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
1c231f56 535
c6878637 536 r = rm_rf(u->runtime_path, REMOVE_ROOT);
20263082 537 if (r < 0)
da927ba9 538 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
20263082
LP
539
540 free(u->runtime_path);
541 u->runtime_path = NULL;
542
543 return r;
544}
545
9bb69af4 546int user_stop(User *u, bool force) {
20263082
LP
547 Session *s;
548 int r = 0, k;
549 assert(u);
550
b58b227a
DH
551 /* Stop jobs have already been queued */
552 if (u->stopping) {
553 user_save(u);
554 return r;
555 }
556
20263082 557 LIST_FOREACH(sessions_by_user, s, u->sessions) {
9bb69af4 558 k = session_stop(s, force);
20263082
LP
559 if (k < 0)
560 r = k;
561 }
562
563 /* Kill systemd */
564 k = user_stop_service(u);
565 if (k < 0)
566 r = k;
567
568 /* Kill cgroup */
fb6becb4 569 k = user_stop_slice(u);
20263082
LP
570 if (k < 0)
571 r = k;
572
5f41d1f1
LP
573 u->stopping = true;
574
405e0255
LP
575 user_save(u);
576
577 return r;
578}
579
580int user_finalize(User *u) {
581 Session *s;
582 int r = 0, k;
583
584 assert(u);
585
586 if (u->started)
587 log_debug("User %s logged out.", u->name);
588
589 LIST_FOREACH(sessions_by_user, s, u->sessions) {
590 k = session_finalize(s);
591 if (k < 0)
592 r = k;
593 }
594
20263082
LP
595 /* Kill XDG_RUNTIME_DIR */
596 k = user_remove_runtime_path(u);
597 if (k < 0)
598 r = k;
599
66cdd0f2
LP
600 /* Clean SysV + POSIX IPC objects */
601 if (u->manager->remove_ipc) {
602 k = clean_ipc(u->uid);
603 if (k < 0)
604 r = k;
605 }
606
d2f92cdf
LP
607 unlink(u->state_file);
608 user_add_to_gc_queue(u);
609
405e0255 610 if (u->started) {
ed18b08b 611 user_send_signal(u, false);
405e0255
LP
612 u->started = false;
613 }
9418f147 614
20263082
LP
615 return r;
616}
617
a185c5aa
LP
618int user_get_idle_hint(User *u, dual_timestamp *t) {
619 Session *s;
620 bool idle_hint = true;
621 dual_timestamp ts = { 0, 0 };
622
623 assert(u);
624
625 LIST_FOREACH(sessions_by_user, s, u->sessions) {
626 dual_timestamp k;
627 int ih;
628
629 ih = session_get_idle_hint(s, &k);
630 if (ih < 0)
631 return ih;
632
633 if (!ih) {
634 if (!idle_hint) {
635 if (k.monotonic < ts.monotonic)
636 ts = k;
637 } else {
638 idle_hint = false;
639 ts = k;
640 }
641 } else if (idle_hint) {
642
643 if (k.monotonic > ts.monotonic)
644 ts = k;
645 }
646 }
647
648 if (t)
649 *t = ts;
650
651 return idle_hint;
652}
653
3a9f7a30 654int user_check_linger_file(User *u) {
cc377381
LP
655 _cleanup_free_ char *cc = NULL;
656 char *p = NULL;
e96cd586 657
cc377381
LP
658 cc = cescape(u->name);
659 if (!cc)
e96cd586
LP
660 return -ENOMEM;
661
63c372cb 662 p = strjoina("/var/lib/systemd/linger/", cc);
e96cd586 663
cc377381 664 return access(p, F_OK) >= 0;
e96cd586
LP
665}
666
cc377381 667bool user_check_gc(User *u, bool drop_not_started) {
20263082
LP
668 assert(u);
669
4a4b033f 670 if (drop_not_started && !u->started)
cc377381 671 return false;
932e3ee7 672
20263082 673 if (u->sessions)
cc377381 674 return true;
20263082 675
e96cd586 676 if (user_check_linger_file(u) > 0)
cc377381 677 return true;
20263082 678
cc377381
LP
679 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
680 return true;
681
682 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
683 return true;
405e0255 684
cc377381 685 return false;
20263082
LP
686}
687
14c3baca
LP
688void user_add_to_gc_queue(User *u) {
689 assert(u);
690
691 if (u->in_gc_queue)
692 return;
693
71fda00f 694 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
14c3baca
LP
695 u->in_gc_queue = true;
696}
697
20263082
LP
698UserState user_get_state(User *u) {
699 Session *i;
700
701 assert(u);
702
5f41d1f1
LP
703 if (u->stopping)
704 return USER_CLOSING;
705
fb6becb4 706 if (u->slice_job || u->service_job)
405e0255 707 return USER_OPENING;
c9caad80 708
5f41d1f1
LP
709 if (u->sessions) {
710 bool all_closing = true;
711
712 LIST_FOREACH(sessions_by_user, i, u->sessions) {
00555a2e
DH
713 SessionState state;
714
715 state = session_get_state(i);
716 if (state == SESSION_ACTIVE)
5f41d1f1 717 return USER_ACTIVE;
00555a2e 718 if (state != SESSION_CLOSING)
5f41d1f1
LP
719 all_closing = false;
720 }
20263082 721
c9caad80 722 return all_closing ? USER_CLOSING : USER_ONLINE;
5f41d1f1 723 }
e96cd586
LP
724
725 if (user_check_linger_file(u) > 0)
726 return USER_LINGERING;
727
728 return USER_CLOSING;
20263082
LP
729}
730
de07ab16 731int user_kill(User *u, int signo) {
de07ab16
LP
732 assert(u);
733
fb6becb4 734 if (!u->slice)
de07ab16
LP
735 return -ESRCH;
736
fb6becb4 737 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
de07ab16
LP
738}
739
952d3260 740void user_elect_display(User *u) {
e9e74f28 741 Session *graphical = NULL, *text = NULL, *other = NULL, *s;
952d3260
LP
742
743 assert(u);
744
745 /* This elects a primary session for each user, which we call
746 * the "display". We try to keep the assignment stable, but we
747 * "upgrade" to better choices. */
748
749 LIST_FOREACH(sessions_by_user, s, u->sessions) {
750
751 if (s->class != SESSION_USER)
752 continue;
753
754 if (s->stopping)
755 continue;
756
757 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
758 graphical = s;
e9e74f28 759 else if (s->type == SESSION_TTY)
952d3260 760 text = s;
e9e74f28
LP
761 else
762 other = s;
952d3260
LP
763 }
764
765 if (graphical &&
766 (!u->display ||
767 u->display->class != SESSION_USER ||
768 u->display->stopping ||
e9e74f28 769 !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
952d3260 770 u->display = graphical;
e9e74f28
LP
771 return;
772 }
952d3260
LP
773
774 if (text &&
775 (!u->display ||
776 u->display->class != SESSION_USER ||
e9e74f28
LP
777 u->display->stopping ||
778 u->display->type != SESSION_TTY)) {
952d3260 779 u->display = text;
e9e74f28
LP
780 return;
781 }
782
783 if (other &&
784 (!u->display ||
785 u->display->class != SESSION_USER ||
786 u->display->stopping))
787 u->display = other;
952d3260
LP
788}
789
20263082
LP
790static const char* const user_state_table[_USER_STATE_MAX] = {
791 [USER_OFFLINE] = "offline",
fb6becb4 792 [USER_OPENING] = "opening",
20263082
LP
793 [USER_LINGERING] = "lingering",
794 [USER_ONLINE] = "online",
e96cd586
LP
795 [USER_ACTIVE] = "active",
796 [USER_CLOSING] = "closing"
20263082
LP
797};
798
799DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
1c231f56
LP
800
801int config_parse_tmpfs_size(
802 const char* unit,
803 const char *filename,
804 unsigned line,
805 const char *section,
806 unsigned section_line,
807 const char *lvalue,
808 int ltype,
809 const char *rvalue,
810 void *data,
811 void *userdata) {
812
813 size_t *sz = data;
814 const char *e;
815 int r;
816
817 assert(filename);
818 assert(lvalue);
819 assert(rvalue);
820 assert(data);
821
822 e = endswith(rvalue, "%");
823 if (e) {
824 unsigned long ul;
825 char *f;
826
827 errno = 0;
828 ul = strtoul(rvalue, &f, 10);
829 if (errno != 0 || f != e) {
830 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
831 return 0;
832 }
833
834 if (ul <= 0 || ul >= 100) {
835 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
836 return 0;
837 }
838
839 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
840 } else {
841 off_t o;
842
843 r = parse_size(rvalue, 1024, &o);
844 if (r < 0 || (off_t) (size_t) o != o) {
845 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
846 return 0;
847 }
848
849 *sz = PAGE_ALIGN((size_t) o);
850 }
851
852 return 0;
853}