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