]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
Revert "systemctl: append default suffix only if none present"
[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"
20263082
LP
29#include "hashmap.h"
30#include "strv.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
9444b1f2
LP
59 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
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
100 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
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);
20263082 313 if (r < 0) {
da927ba9 314 log_error_errno(r, "Failed to create /run/user: %m");
20263082
LP
315 return r;
316 }
317
318 if (!u->runtime_path) {
1c231f56 319 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
0d0f0c50 320 return log_oom();
20263082
LP
321 } else
322 p = u->runtime_path;
323
1c231f56
LP
324 if (path_is_mount_point(p, false) <= 0) {
325 _cleanup_free_ char *t = NULL;
326
327 mkdir(p, 0700);
328
6baa7db0 329 if (mac_smack_use())
374738d5
LS
330 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
331 else
332 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
333
334 if (r < 0) {
1c231f56
LP
335 r = log_oom();
336 goto fail;
337 }
338
339 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
340 if (r < 0) {
da927ba9 341 log_error_errno(r, "Failed to mount per-user tmpfs directory %s: %m", p);
1c231f56
LP
342 goto fail;
343 }
20263082
LP
344 }
345
346 u->runtime_path = p;
347 return 0;
1c231f56
LP
348
349fail:
350 free(p);
351 u->runtime_path = NULL;
352 return r;
20263082
LP
353}
354
fb6becb4 355static int user_start_slice(User *u) {
fb6becb4 356 char *job;
20263082
LP
357 int r;
358
359 assert(u);
360
fb6becb4 361 if (!u->slice) {
cc377381 362 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de0671ee
ZJS
363 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
364 sprintf(lu, UID_FMT, u->uid);
ae018d9b 365
d0af76e6 366 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
9444b1f2
LP
367 if (r < 0)
368 return r;
369
d0af76e6
LP
370 r = manager_start_unit(u->manager, slice, &error, &job);
371 if (r < 0) {
cc377381 372 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
d0af76e6
LP
373 free(slice);
374 } else {
375 u->slice = slice;
376
377 free(u->slice_job);
378 u->slice_job = job;
379 }
20263082
LP
380 }
381
d0af76e6
LP
382 if (u->slice)
383 hashmap_put(u->manager->user_units, u->slice, u);
384
20263082
LP
385 return 0;
386}
387
388static int user_start_service(User *u) {
cc377381 389 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
390 char *job;
391 int r;
392
20263082
LP
393 assert(u);
394
fb6becb4 395 if (!u->service) {
de0671ee
ZJS
396 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
397 sprintf(lu, UID_FMT, u->uid);
fb6becb4 398
d0af76e6
LP
399 service = unit_name_build("user", lu, ".service");
400 if (!service)
fb6becb4
LP
401 return log_oom();
402
d0af76e6
LP
403 r = manager_start_unit(u->manager, service, &error, &job);
404 if (r < 0) {
cc377381 405 log_error("Failed to start user service: %s", bus_error_message(&error, r));
d0af76e6
LP
406 free(service);
407 } else {
408 u->service = service;
409
410 free(u->service_job);
411 u->service_job = job;
412 }
fb6becb4
LP
413 }
414
d0af76e6
LP
415 if (u->service)
416 hashmap_put(u->manager->user_units, u->service, u);
417
20263082
LP
418 return 0;
419}
420
421int user_start(User *u) {
422 int r;
423
424 assert(u);
425
9418f147
LP
426 if (u->started)
427 return 0;
428
1637a8be 429 log_debug("New user %s logged in.", u->name);
ed18b08b 430
20263082
LP
431 /* Make XDG_RUNTIME_DIR */
432 r = user_mkdir_runtime_path(u);
433 if (r < 0)
434 return r;
435
436 /* Create cgroup */
fb6becb4 437 r = user_start_slice(u);
20263082
LP
438 if (r < 0)
439 return r;
440
441 /* Spawn user systemd */
442 r = user_start_service(u);
443 if (r < 0)
444 return r;
445
9444b1f2
LP
446 if (!dual_timestamp_is_set(&u->timestamp))
447 dual_timestamp_get(&u->timestamp);
20263082 448
9418f147
LP
449 u->started = true;
450
7f7bb946
LP
451 /* Save new user data */
452 user_save(u);
453
da119395
LP
454 user_send_signal(u, true);
455
20263082
LP
456 return 0;
457}
458
fb6becb4 459static int user_stop_slice(User *u) {
cc377381 460 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
461 char *job;
462 int r;
20263082 463
20263082
LP
464 assert(u);
465
fb6becb4
LP
466 if (!u->slice)
467 return 0;
468
469 r = manager_stop_unit(u->manager, u->slice, &error, &job);
470 if (r < 0) {
cc377381 471 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
472 return r;
473 }
ed18b08b 474
fb6becb4
LP
475 free(u->slice_job);
476 u->slice_job = job;
ed18b08b 477
fb6becb4 478 return r;
20263082
LP
479}
480
fb6becb4 481static int user_stop_service(User *u) {
cc377381 482 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 483 char *job;
20263082 484 int r;
20263082
LP
485
486 assert(u);
487
fb6becb4 488 if (!u->service)
20263082
LP
489 return 0;
490
fb6becb4
LP
491 r = manager_stop_unit(u->manager, u->service, &error, &job);
492 if (r < 0) {
cc377381 493 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
fb6becb4
LP
494 return r;
495 }
20263082 496
fb6becb4
LP
497 free(u->service_job);
498 u->service_job = job;
20263082 499
fb6becb4
LP
500 return r;
501}
20263082 502
20263082
LP
503static int user_remove_runtime_path(User *u) {
504 int r;
505
506 assert(u);
507
508 if (!u->runtime_path)
509 return 0;
510
1c231f56
LP
511 r = rm_rf(u->runtime_path, false, false, false);
512 if (r < 0)
da927ba9 513 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
1c231f56
LP
514
515 if (umount2(u->runtime_path, MNT_DETACH) < 0)
516 log_error("Failed to unmount user runtime directory %s: %m", u->runtime_path);
517
ad293f5a 518 r = rm_rf(u->runtime_path, false, true, false);
20263082 519 if (r < 0)
da927ba9 520 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
20263082
LP
521
522 free(u->runtime_path);
523 u->runtime_path = NULL;
524
525 return r;
526}
527
9bb69af4 528int user_stop(User *u, bool force) {
20263082
LP
529 Session *s;
530 int r = 0, k;
531 assert(u);
532
b58b227a
DH
533 /* Stop jobs have already been queued */
534 if (u->stopping) {
535 user_save(u);
536 return r;
537 }
538
20263082 539 LIST_FOREACH(sessions_by_user, s, u->sessions) {
9bb69af4 540 k = session_stop(s, force);
20263082
LP
541 if (k < 0)
542 r = k;
543 }
544
545 /* Kill systemd */
546 k = user_stop_service(u);
547 if (k < 0)
548 r = k;
549
550 /* Kill cgroup */
fb6becb4 551 k = user_stop_slice(u);
20263082
LP
552 if (k < 0)
553 r = k;
554
5f41d1f1
LP
555 u->stopping = true;
556
405e0255
LP
557 user_save(u);
558
559 return r;
560}
561
562int user_finalize(User *u) {
563 Session *s;
564 int r = 0, k;
565
566 assert(u);
567
568 if (u->started)
569 log_debug("User %s logged out.", u->name);
570
571 LIST_FOREACH(sessions_by_user, s, u->sessions) {
572 k = session_finalize(s);
573 if (k < 0)
574 r = k;
575 }
576
20263082
LP
577 /* Kill XDG_RUNTIME_DIR */
578 k = user_remove_runtime_path(u);
579 if (k < 0)
580 r = k;
581
66cdd0f2
LP
582 /* Clean SysV + POSIX IPC objects */
583 if (u->manager->remove_ipc) {
584 k = clean_ipc(u->uid);
585 if (k < 0)
586 r = k;
587 }
588
d2f92cdf
LP
589 unlink(u->state_file);
590 user_add_to_gc_queue(u);
591
405e0255 592 if (u->started) {
ed18b08b 593 user_send_signal(u, false);
405e0255
LP
594 u->started = false;
595 }
9418f147 596
20263082
LP
597 return r;
598}
599
a185c5aa
LP
600int user_get_idle_hint(User *u, dual_timestamp *t) {
601 Session *s;
602 bool idle_hint = true;
603 dual_timestamp ts = { 0, 0 };
604
605 assert(u);
606
607 LIST_FOREACH(sessions_by_user, s, u->sessions) {
608 dual_timestamp k;
609 int ih;
610
611 ih = session_get_idle_hint(s, &k);
612 if (ih < 0)
613 return ih;
614
615 if (!ih) {
616 if (!idle_hint) {
617 if (k.monotonic < ts.monotonic)
618 ts = k;
619 } else {
620 idle_hint = false;
621 ts = k;
622 }
623 } else if (idle_hint) {
624
625 if (k.monotonic > ts.monotonic)
626 ts = k;
627 }
628 }
629
630 if (t)
631 *t = ts;
632
633 return idle_hint;
634}
635
3a9f7a30 636int user_check_linger_file(User *u) {
cc377381
LP
637 _cleanup_free_ char *cc = NULL;
638 char *p = NULL;
e96cd586 639
cc377381
LP
640 cc = cescape(u->name);
641 if (!cc)
e96cd586
LP
642 return -ENOMEM;
643
cc377381 644 p = strappenda("/var/lib/systemd/linger/", cc);
e96cd586 645
cc377381 646 return access(p, F_OK) >= 0;
e96cd586
LP
647}
648
cc377381 649bool user_check_gc(User *u, bool drop_not_started) {
20263082
LP
650 assert(u);
651
4a4b033f 652 if (drop_not_started && !u->started)
cc377381 653 return false;
932e3ee7 654
20263082 655 if (u->sessions)
cc377381 656 return true;
20263082 657
e96cd586 658 if (user_check_linger_file(u) > 0)
cc377381 659 return true;
20263082 660
cc377381
LP
661 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
662 return true;
663
664 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
665 return true;
405e0255 666
cc377381 667 return false;
20263082
LP
668}
669
14c3baca
LP
670void user_add_to_gc_queue(User *u) {
671 assert(u);
672
673 if (u->in_gc_queue)
674 return;
675
71fda00f 676 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
14c3baca
LP
677 u->in_gc_queue = true;
678}
679
20263082
LP
680UserState user_get_state(User *u) {
681 Session *i;
682
683 assert(u);
684
5f41d1f1
LP
685 if (u->stopping)
686 return USER_CLOSING;
687
fb6becb4 688 if (u->slice_job || u->service_job)
405e0255 689 return USER_OPENING;
c9caad80 690
5f41d1f1
LP
691 if (u->sessions) {
692 bool all_closing = true;
693
694 LIST_FOREACH(sessions_by_user, i, u->sessions) {
00555a2e
DH
695 SessionState state;
696
697 state = session_get_state(i);
698 if (state == SESSION_ACTIVE)
5f41d1f1 699 return USER_ACTIVE;
00555a2e 700 if (state != SESSION_CLOSING)
5f41d1f1
LP
701 all_closing = false;
702 }
20263082 703
c9caad80 704 return all_closing ? USER_CLOSING : USER_ONLINE;
5f41d1f1 705 }
e96cd586
LP
706
707 if (user_check_linger_file(u) > 0)
708 return USER_LINGERING;
709
710 return USER_CLOSING;
20263082
LP
711}
712
de07ab16 713int user_kill(User *u, int signo) {
de07ab16
LP
714 assert(u);
715
fb6becb4 716 if (!u->slice)
de07ab16
LP
717 return -ESRCH;
718
fb6becb4 719 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
de07ab16
LP
720}
721
952d3260 722void user_elect_display(User *u) {
e9e74f28 723 Session *graphical = NULL, *text = NULL, *other = NULL, *s;
952d3260
LP
724
725 assert(u);
726
727 /* This elects a primary session for each user, which we call
728 * the "display". We try to keep the assignment stable, but we
729 * "upgrade" to better choices. */
730
731 LIST_FOREACH(sessions_by_user, s, u->sessions) {
732
733 if (s->class != SESSION_USER)
734 continue;
735
736 if (s->stopping)
737 continue;
738
739 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
740 graphical = s;
e9e74f28 741 else if (s->type == SESSION_TTY)
952d3260 742 text = s;
e9e74f28
LP
743 else
744 other = s;
952d3260
LP
745 }
746
747 if (graphical &&
748 (!u->display ||
749 u->display->class != SESSION_USER ||
750 u->display->stopping ||
e9e74f28 751 !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
952d3260 752 u->display = graphical;
e9e74f28
LP
753 return;
754 }
952d3260
LP
755
756 if (text &&
757 (!u->display ||
758 u->display->class != SESSION_USER ||
e9e74f28
LP
759 u->display->stopping ||
760 u->display->type != SESSION_TTY)) {
952d3260 761 u->display = text;
e9e74f28
LP
762 return;
763 }
764
765 if (other &&
766 (!u->display ||
767 u->display->class != SESSION_USER ||
768 u->display->stopping))
769 u->display = other;
952d3260
LP
770}
771
20263082
LP
772static const char* const user_state_table[_USER_STATE_MAX] = {
773 [USER_OFFLINE] = "offline",
fb6becb4 774 [USER_OPENING] = "opening",
20263082
LP
775 [USER_LINGERING] = "lingering",
776 [USER_ONLINE] = "online",
e96cd586
LP
777 [USER_ACTIVE] = "active",
778 [USER_CLOSING] = "closing"
20263082
LP
779};
780
781DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
1c231f56
LP
782
783int config_parse_tmpfs_size(
784 const char* unit,
785 const char *filename,
786 unsigned line,
787 const char *section,
788 unsigned section_line,
789 const char *lvalue,
790 int ltype,
791 const char *rvalue,
792 void *data,
793 void *userdata) {
794
795 size_t *sz = data;
796 const char *e;
797 int r;
798
799 assert(filename);
800 assert(lvalue);
801 assert(rvalue);
802 assert(data);
803
804 e = endswith(rvalue, "%");
805 if (e) {
806 unsigned long ul;
807 char *f;
808
809 errno = 0;
810 ul = strtoul(rvalue, &f, 10);
811 if (errno != 0 || f != e) {
812 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
813 return 0;
814 }
815
816 if (ul <= 0 || ul >= 100) {
817 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
818 return 0;
819 }
820
821 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
822 } else {
823 off_t o;
824
825 r = parse_size(rvalue, 1024, &o);
826 if (r < 0 || (off_t) (size_t) o != o) {
827 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
828 return 0;
829 }
830
831 *sz = PAGE_ALIGN((size_t) o);
832 }
833
834 return 0;
835}