chiark / gitweb /
2733117c641977de41cf08f74e52a7d886eb2d2e
[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(SPECIAL_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 #endif // 0
420                 if (r < 0) {
421                         log_error("Failed to start user slice: %s", bus_error_message(&error, r));
422                         free(slice);
423                 } else {
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 #endif // 0
431                 }
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         assert(u);
797
798 /// FIXME: Without direct cgroup support, elogind can not kill users
799 #if 0
800         if (!u->slice)
801                 return -ESRCH;
802
803         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
804 #else
805         return -ESRCH;
806 #endif // 0
807 }
808
809 static bool elect_display_filter(Session *s) {
810         /* Return true if the session is a candidate for the user’s ‘primary
811          * session’ or ‘display’. */
812         assert(s);
813
814         return (s->class == SESSION_USER && !s->stopping);
815 }
816
817 static int elect_display_compare(Session *s1, Session *s2) {
818         /* Indexed by SessionType. Lower numbers mean more preferred. */
819         const int type_ranks[_SESSION_TYPE_MAX] = {
820                 [SESSION_UNSPECIFIED] = 0,
821                 [SESSION_TTY] = -2,
822                 [SESSION_X11] = -3,
823                 [SESSION_WAYLAND] = -3,
824                 [SESSION_MIR] = -3,
825                 [SESSION_WEB] = -1,
826         };
827
828         /* Calculate the partial order relationship between s1 and s2,
829          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
830          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
831          * is preferred.
832          *
833          * s1 or s2 may be NULL. */
834         if (!s1 && !s2)
835                 return 0;
836
837         if ((s1 == NULL) != (s2 == NULL))
838                 return (s1 == NULL) - (s2 == NULL);
839
840         if (s1->stopping != s2->stopping)
841                 return s1->stopping - s2->stopping;
842
843         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
844                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
845
846         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
847                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
848
849         if (s1->type != s2->type)
850                 return type_ranks[s1->type] - type_ranks[s2->type];
851
852         return 0;
853 }
854
855 void user_elect_display(User *u) {
856         Session *s;
857
858         assert(u);
859
860         /* This elects a primary session for each user, which we call
861          * the "display". We try to keep the assignment stable, but we
862          * "upgrade" to better choices. */
863         log_debug("Electing new display for user %s", u->name);
864
865         LIST_FOREACH(sessions_by_user, s, u->sessions) {
866                 if (!elect_display_filter(s)) {
867                         log_debug("Ignoring session %s", s->id);
868                         continue;
869                 }
870
871                 if (elect_display_compare(s, u->display) < 0) {
872                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
873                         u->display = s;
874                 }
875         }
876 }
877
878 static const char* const user_state_table[_USER_STATE_MAX] = {
879         [USER_OFFLINE] = "offline",
880         [USER_OPENING] = "opening",
881         [USER_LINGERING] = "lingering",
882         [USER_ONLINE] = "online",
883         [USER_ACTIVE] = "active",
884         [USER_CLOSING] = "closing"
885 };
886
887 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
888
889 int config_parse_tmpfs_size(
890                 const char* unit,
891                 const char *filename,
892                 unsigned line,
893                 const char *section,
894                 unsigned section_line,
895                 const char *lvalue,
896                 int ltype,
897                 const char *rvalue,
898                 void *data,
899                 void *userdata) {
900
901         size_t *sz = data;
902         const char *e;
903         int r;
904
905         assert(filename);
906         assert(lvalue);
907         assert(rvalue);
908         assert(data);
909
910         e = endswith(rvalue, "%");
911         if (e) {
912                 unsigned long ul;
913                 char *f;
914
915                 errno = 0;
916                 ul = strtoul(rvalue, &f, 10);
917                 if (errno != 0 || f != e) {
918                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
919                         return 0;
920                 }
921
922                 if (ul <= 0 || ul >= 100) {
923                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
924                         return 0;
925                 }
926
927                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
928         } else {
929                 off_t o;
930
931                 r = parse_size(rvalue, 1024, &o);
932                 if (r < 0 || (off_t) (size_t) o != o) {
933                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
934                         return 0;
935                 }
936
937                 *sz = PAGE_ALIGN((size_t) o);
938         }
939
940         return 0;
941 }