chiark / gitweb /
Classify processes from sessions into cgroups
[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", 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         free(u->runtime_path);
594         u->runtime_path = NULL;
595
596         return r;
597 }
598
599 int user_stop(User *u, bool force) {
600         Session *s;
601         int r = 0, k;
602         assert(u);
603
604         /* Stop jobs have already been queued */
605         if (u->stopping) {
606                 user_save(u);
607                 return r;
608         }
609
610         LIST_FOREACH(sessions_by_user, s, u->sessions) {
611                 k = session_stop(s, force);
612                 if (k < 0)
613                         r = k;
614         }
615
616         /* Kill systemd */
617 /// elogind does not support service or slice jobs
618 #if 0
619         k = user_stop_service(u);
620         if (k < 0)
621                 r = k;
622
623         /* Kill cgroup */
624         k = user_stop_slice(u);
625         if (k < 0)
626                 r = k;
627 #endif // 0
628
629         u->stopping = true;
630
631         user_save(u);
632
633         return r;
634 }
635
636 int user_finalize(User *u) {
637         Session *s;
638         int r = 0, k;
639
640         assert(u);
641
642         if (u->started)
643                 log_debug("User %s logged out.", u->name);
644
645         LIST_FOREACH(sessions_by_user, s, u->sessions) {
646                 k = session_finalize(s);
647                 if (k < 0)
648                         r = k;
649         }
650
651         /* Kill XDG_RUNTIME_DIR */
652         k = user_remove_runtime_path(u);
653         if (k < 0)
654                 r = k;
655
656         /* Clean SysV + POSIX IPC objects */
657         if (u->manager->remove_ipc) {
658                 k = clean_ipc(u->uid);
659                 if (k < 0)
660                         r = k;
661         }
662
663         unlink(u->state_file);
664         user_add_to_gc_queue(u);
665
666         if (u->started) {
667                 user_send_signal(u, false);
668                 u->started = false;
669         }
670
671         return r;
672 }
673
674 int user_get_idle_hint(User *u, dual_timestamp *t) {
675         Session *s;
676         bool idle_hint = true;
677         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
678
679         assert(u);
680
681         LIST_FOREACH(sessions_by_user, s, u->sessions) {
682                 dual_timestamp k;
683                 int ih;
684
685                 ih = session_get_idle_hint(s, &k);
686                 if (ih < 0)
687                         return ih;
688
689                 if (!ih) {
690                         if (!idle_hint) {
691                                 if (k.monotonic < ts.monotonic)
692                                         ts = k;
693                         } else {
694                                 idle_hint = false;
695                                 ts = k;
696                         }
697                 } else if (idle_hint) {
698
699                         if (k.monotonic > ts.monotonic)
700                                 ts = k;
701                 }
702         }
703
704         if (t)
705                 *t = ts;
706
707         return idle_hint;
708 }
709
710 int user_check_linger_file(User *u) {
711         _cleanup_free_ char *cc = NULL;
712         char *p = NULL;
713
714         cc = cescape(u->name);
715         if (!cc)
716                 return -ENOMEM;
717
718         p = strjoina("/var/lib/systemd/linger/", cc);
719
720         return access(p, F_OK) >= 0;
721 }
722
723 bool user_check_gc(User *u, bool drop_not_started) {
724         assert(u);
725
726         if (drop_not_started && !u->started)
727                 return false;
728
729         if (u->sessions)
730                 return true;
731
732         if (user_check_linger_file(u) > 0)
733                 return true;
734
735 /// elogind does not support systemd services and slices
736 #if 0
737         if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
738                 return true;
739
740         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
741                 return true;
742 #endif // 0
743
744         return false;
745 }
746
747 void user_add_to_gc_queue(User *u) {
748         assert(u);
749
750         if (u->in_gc_queue)
751                 return;
752
753         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
754         u->in_gc_queue = true;
755 }
756
757 UserState user_get_state(User *u) {
758         Session *i;
759
760         assert(u);
761
762         if (u->stopping)
763                 return USER_CLOSING;
764
765 /// elogind does not support slice and service jobs
766 #if 0
767         if (!u->started || u->slice_job || u->service_job)
768 #else
769         if (!u->started)
770 #endif // 0
771                 return USER_OPENING;
772
773         if (u->sessions) {
774                 bool all_closing = true;
775
776                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
777                         SessionState state;
778
779                         state = session_get_state(i);
780                         if (state == SESSION_ACTIVE)
781                                 return USER_ACTIVE;
782                         if (state != SESSION_CLOSING)
783                                 all_closing = false;
784                 }
785
786                 return all_closing ? USER_CLOSING : USER_ONLINE;
787         }
788
789         if (user_check_linger_file(u) > 0)
790                 return USER_LINGERING;
791
792         return USER_CLOSING;
793 }
794
795 int user_kill(User *u, int signo) {
796 /// Without systemd unit support, elogind has to rely on its session system
797 #if 0
798         assert(u);
799
800         if (!u->slice)
801                 return -ESRCH;
802
803         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
804 #else
805         Session *s;
806         int res = 0;
807
808         assert(u);
809
810         LIST_FOREACH(sessions_by_user, s, u->sessions) {
811                 int r = session_kill(s, KILL_ALL, signo);
812                 if (res == 0 && r < 0)
813                         res = r;
814         }
815
816         return res;
817 #endif // 0
818 }
819
820 static bool elect_display_filter(Session *s) {
821         /* Return true if the session is a candidate for the user’s ‘primary
822          * session’ or ‘display’. */
823         assert(s);
824
825         return (s->class == SESSION_USER && !s->stopping);
826 }
827
828 static int elect_display_compare(Session *s1, Session *s2) {
829         /* Indexed by SessionType. Lower numbers mean more preferred. */
830         const int type_ranks[_SESSION_TYPE_MAX] = {
831                 [SESSION_UNSPECIFIED] = 0,
832                 [SESSION_TTY] = -2,
833                 [SESSION_X11] = -3,
834                 [SESSION_WAYLAND] = -3,
835                 [SESSION_MIR] = -3,
836                 [SESSION_WEB] = -1,
837         };
838
839         /* Calculate the partial order relationship between s1 and s2,
840          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
841          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
842          * is preferred.
843          *
844          * s1 or s2 may be NULL. */
845         if (!s1 && !s2)
846                 return 0;
847
848         if ((s1 == NULL) != (s2 == NULL))
849                 return (s1 == NULL) - (s2 == NULL);
850
851         if (s1->stopping != s2->stopping)
852                 return s1->stopping - s2->stopping;
853
854         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
855                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
856
857         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
858                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
859
860         if (s1->type != s2->type)
861                 return type_ranks[s1->type] - type_ranks[s2->type];
862
863         return 0;
864 }
865
866 void user_elect_display(User *u) {
867         Session *s;
868
869         assert(u);
870
871         /* This elects a primary session for each user, which we call
872          * the "display". We try to keep the assignment stable, but we
873          * "upgrade" to better choices. */
874         log_debug("Electing new display for user %s", u->name);
875
876         LIST_FOREACH(sessions_by_user, s, u->sessions) {
877                 if (!elect_display_filter(s)) {
878                         log_debug("Ignoring session %s", s->id);
879                         continue;
880                 }
881
882                 if (elect_display_compare(s, u->display) < 0) {
883                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
884                         u->display = s;
885                 }
886         }
887 }
888
889 static const char* const user_state_table[_USER_STATE_MAX] = {
890         [USER_OFFLINE] = "offline",
891         [USER_OPENING] = "opening",
892         [USER_LINGERING] = "lingering",
893         [USER_ONLINE] = "online",
894         [USER_ACTIVE] = "active",
895         [USER_CLOSING] = "closing"
896 };
897
898 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
899
900 int config_parse_tmpfs_size(
901                 const char* unit,
902                 const char *filename,
903                 unsigned line,
904                 const char *section,
905                 unsigned section_line,
906                 const char *lvalue,
907                 int ltype,
908                 const char *rvalue,
909                 void *data,
910                 void *userdata) {
911
912         size_t *sz = data;
913         const char *e;
914         int r;
915
916         assert(filename);
917         assert(lvalue);
918         assert(rvalue);
919         assert(data);
920
921         e = endswith(rvalue, "%");
922         if (e) {
923                 unsigned long ul;
924                 char *f;
925
926                 errno = 0;
927                 ul = strtoul(rvalue, &f, 10);
928                 if (errno != 0 || f != e) {
929                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
930                         return 0;
931                 }
932
933                 if (ul <= 0 || ul >= 100) {
934                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
935                         return 0;
936                 }
937
938                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
939         } else {
940                 off_t o;
941
942                 r = parse_size(rvalue, 1024, &o);
943                 if (r < 0 || (off_t) (size_t) o != o) {
944                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
945                         return 0;
946                 }
947
948                 *sz = PAGE_ALIGN((size_t) o);
949         }
950
951         return 0;
952 }