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