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