chiark / gitweb /
logind: minor clean-ups
[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 && !u->stopping)
485                 return 0;
486
487         /*
488          * If u->stopping is set, the user is marked for removal and the slice
489          * and service stop-jobs are queued. We have to clear that flag before
490          * queing the start-jobs again. If they succeed, the user object can be
491          * re-used just fine (pid1 takes care of job-ordering and proper
492          * restart), but if they fail, we want to force another user_stop() so
493          * possibly pending units are stopped.
494          * Note that we don't clear u->started, as we have no clue what state
495          * the user is in on failure here. Hence, we pretend the user is
496          * running so it will be properly taken down by GC. However, we clearly
497          * return an error from user_start() in that case, so no further
498          * reference to the user is taken.
499          */
500         u->stopping = false;
501
502         if (!u->started) {
503                 log_debug("New user %s logged in.", u->name);
504
505                 /* Make XDG_RUNTIME_DIR */
506                 r = user_mkdir_runtime_path(u);
507                 if (r < 0)
508                         return r;
509         }
510
511         /* Create cgroup */
512         r = user_start_slice(u);
513         if (r < 0)
514                 return r;
515
516         /* Save the user data so far, because pam_systemd will read the
517          * XDG_RUNTIME_DIR out of it while starting up systemd --user.
518          * We need to do user_save_internal() because we have not
519          * "officially" started yet. */
520         user_save_internal(u);
521
522         /* Spawn user systemd */
523         r = user_start_service(u);
524         if (r < 0)
525                 return r;
526
527         if (!u->started) {
528                 if (!dual_timestamp_is_set(&u->timestamp))
529                         dual_timestamp_get(&u->timestamp);
530                 user_send_signal(u, true);
531                 u->started = true;
532         }
533
534         /* Save new user data */
535         user_save(u);
536
537         return 0;
538 }
539
540 /// UNNEEDED by elogind
541 #if 0
542 static int user_stop_slice(User *u) {
543         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
544         // char *job;
545         int r = 0;
546
547         assert(u);
548
549         if (!u->slice)
550                 return 0;
551
552         r = manager_stop_unit(u->manager, u->slice, &error, &job);
553         if (r < 0) {
554                 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
555                 return r;
556         }
557
558         free(u->slice_job);
559         u->slice_job = job;
560
561         return r;
562 }
563
564 static int user_stop_service(User *u) {
565         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
566         // char *job;
567         int r = 0;
568
569         assert(u);
570
571         if (!u->service)
572                 return 0;
573
574         r = manager_stop_unit(u->manager, u->service, &error, &job);
575         if (r < 0) {
576                 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
577                 return r;
578         }
579
580         free(u->service_job);
581         u->service_job = job;
582
583         return r;
584 }
585 #endif // 0
586
587 static int user_remove_runtime_path(User *u) {
588         int r;
589
590         assert(u);
591
592         if (!u->runtime_path)
593                 return 0;
594
595         r = rm_rf(u->runtime_path, 0);
596         if (r < 0)
597                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
598
599         /* Ignore cases where the directory isn't mounted, as that's
600          * quite possible, if we lacked the permissions to mount
601          * something */
602         r = umount2(u->runtime_path, MNT_DETACH);
603         if (r < 0 && errno != EINVAL && errno != ENOENT)
604                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
605
606         r = rm_rf(u->runtime_path, REMOVE_ROOT);
607         if (r < 0)
608                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
609
610         u->runtime_path = mfree(u->runtime_path);
611
612         return r;
613 }
614
615 int user_stop(User *u, bool force) {
616         Session *s;
617         int r = 0, k;
618         assert(u);
619
620         /* Stop jobs have already been queued */
621         if (u->stopping) {
622                 user_save(u);
623                 return r;
624         }
625
626         LIST_FOREACH(sessions_by_user, s, u->sessions) {
627                 k = session_stop(s, force);
628                 if (k < 0)
629                         r = k;
630         }
631
632         /* Kill systemd */
633 /// elogind does not support service or slice jobs
634 #if 0
635         k = user_stop_service(u);
636         if (k < 0)
637                 r = k;
638
639         /* Kill cgroup */
640         k = user_stop_slice(u);
641         if (k < 0)
642                 r = k;
643 #endif // 0
644
645         u->stopping = true;
646
647         user_save(u);
648
649         return r;
650 }
651
652 int user_finalize(User *u) {
653         Session *s;
654         int r = 0, k;
655
656         assert(u);
657
658         if (u->started)
659                 log_debug("User %s logged out.", u->name);
660
661         LIST_FOREACH(sessions_by_user, s, u->sessions) {
662                 k = session_finalize(s);
663                 if (k < 0)
664                         r = k;
665         }
666
667         /* Kill XDG_RUNTIME_DIR */
668         k = user_remove_runtime_path(u);
669         if (k < 0)
670                 r = k;
671
672         /* Clean SysV + POSIX IPC objects */
673         if (u->manager->remove_ipc) {
674                 k = clean_ipc(u->uid);
675                 if (k < 0)
676                         r = k;
677         }
678
679         unlink(u->state_file);
680         user_add_to_gc_queue(u);
681
682         if (u->started) {
683                 user_send_signal(u, false);
684                 u->started = false;
685         }
686
687         return r;
688 }
689
690 int user_get_idle_hint(User *u, dual_timestamp *t) {
691         Session *s;
692         bool idle_hint = true;
693         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
694
695         assert(u);
696
697         LIST_FOREACH(sessions_by_user, s, u->sessions) {
698                 dual_timestamp k;
699                 int ih;
700
701                 ih = session_get_idle_hint(s, &k);
702                 if (ih < 0)
703                         return ih;
704
705                 if (!ih) {
706                         if (!idle_hint) {
707                                 if (k.monotonic < ts.monotonic)
708                                         ts = k;
709                         } else {
710                                 idle_hint = false;
711                                 ts = k;
712                         }
713                 } else if (idle_hint) {
714
715                         if (k.monotonic > ts.monotonic)
716                                 ts = k;
717                 }
718         }
719
720         if (t)
721                 *t = ts;
722
723         return idle_hint;
724 }
725
726 int user_check_linger_file(User *u) {
727         _cleanup_free_ char *cc = NULL;
728         char *p = NULL;
729
730         cc = cescape(u->name);
731         if (!cc)
732                 return -ENOMEM;
733
734         p = strjoina("/var/lib/systemd/linger/", cc);
735
736         return access(p, F_OK) >= 0;
737 }
738
739 bool user_check_gc(User *u, bool drop_not_started) {
740         assert(u);
741
742         if (drop_not_started && !u->started)
743                 return false;
744
745         if (u->sessions)
746                 return true;
747
748         if (user_check_linger_file(u) > 0)
749                 return true;
750
751 /// elogind does not support systemd services and slices
752 #if 0
753         if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
754                 return true;
755
756         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
757                 return true;
758 #endif // 0
759
760         return false;
761 }
762
763 void user_add_to_gc_queue(User *u) {
764         assert(u);
765
766         if (u->in_gc_queue)
767                 return;
768
769         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
770         u->in_gc_queue = true;
771 }
772
773 UserState user_get_state(User *u) {
774         Session *i;
775
776         assert(u);
777
778         if (u->stopping)
779                 return USER_CLOSING;
780
781 /// elogind does not support slice and service jobs
782 #if 0
783         if (!u->started || u->slice_job || u->service_job)
784 #else
785         if (!u->started)
786 #endif // 0
787                 return USER_OPENING;
788
789         if (u->sessions) {
790                 bool all_closing = true;
791
792                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
793                         SessionState state;
794
795                         state = session_get_state(i);
796                         if (state == SESSION_ACTIVE)
797                                 return USER_ACTIVE;
798                         if (state != SESSION_CLOSING)
799                                 all_closing = false;
800                 }
801
802                 return all_closing ? USER_CLOSING : USER_ONLINE;
803         }
804
805         if (user_check_linger_file(u) > 0)
806                 return USER_LINGERING;
807
808         return USER_CLOSING;
809 }
810
811 int user_kill(User *u, int signo) {
812 /// Without systemd unit support, elogind has to rely on its session system
813 #if 0
814         assert(u);
815
816         if (!u->slice)
817                 return -ESRCH;
818
819         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
820 #else
821         Session *s;
822         int res = 0;
823
824         assert(u);
825
826         LIST_FOREACH(sessions_by_user, s, u->sessions) {
827                 int r = session_kill(s, KILL_ALL, signo);
828                 if (res == 0 && r < 0)
829                         res = r;
830         }
831
832         return res;
833 #endif // 0
834 }
835
836 static bool elect_display_filter(Session *s) {
837         /* Return true if the session is a candidate for the user’s ‘primary
838          * session’ or ‘display’. */
839         assert(s);
840
841         return (s->class == SESSION_USER && !s->stopping);
842 }
843
844 static int elect_display_compare(Session *s1, Session *s2) {
845         /* Indexed by SessionType. Lower numbers mean more preferred. */
846         const int type_ranks[_SESSION_TYPE_MAX] = {
847                 [SESSION_UNSPECIFIED] = 0,
848                 [SESSION_TTY] = -2,
849                 [SESSION_X11] = -3,
850                 [SESSION_WAYLAND] = -3,
851                 [SESSION_MIR] = -3,
852                 [SESSION_WEB] = -1,
853         };
854
855         /* Calculate the partial order relationship between s1 and s2,
856          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
857          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
858          * is preferred.
859          *
860          * s1 or s2 may be NULL. */
861         if (!s1 && !s2)
862                 return 0;
863
864         if ((s1 == NULL) != (s2 == NULL))
865                 return (s1 == NULL) - (s2 == NULL);
866
867         if (s1->stopping != s2->stopping)
868                 return s1->stopping - s2->stopping;
869
870         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
871                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
872
873         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
874                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
875
876         if (s1->type != s2->type)
877                 return type_ranks[s1->type] - type_ranks[s2->type];
878
879         return 0;
880 }
881
882 void user_elect_display(User *u) {
883         Session *s;
884
885         assert(u);
886
887         /* This elects a primary session for each user, which we call
888          * the "display". We try to keep the assignment stable, but we
889          * "upgrade" to better choices. */
890         log_debug("Electing new display for user %s", u->name);
891
892         LIST_FOREACH(sessions_by_user, s, u->sessions) {
893                 if (!elect_display_filter(s)) {
894                         log_debug("Ignoring session %s", s->id);
895                         continue;
896                 }
897
898                 if (elect_display_compare(s, u->display) < 0) {
899                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
900                         u->display = s;
901                 }
902         }
903 }
904
905 static const char* const user_state_table[_USER_STATE_MAX] = {
906         [USER_OFFLINE] = "offline",
907         [USER_OPENING] = "opening",
908         [USER_LINGERING] = "lingering",
909         [USER_ONLINE] = "online",
910         [USER_ACTIVE] = "active",
911         [USER_CLOSING] = "closing"
912 };
913
914 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
915
916 int config_parse_tmpfs_size(
917                 const char* unit,
918                 const char *filename,
919                 unsigned line,
920                 const char *section,
921                 unsigned section_line,
922                 const char *lvalue,
923                 int ltype,
924                 const char *rvalue,
925                 void *data,
926                 void *userdata) {
927
928         size_t *sz = data;
929         const char *e;
930         int r;
931
932         assert(filename);
933         assert(lvalue);
934         assert(rvalue);
935         assert(data);
936
937         e = endswith(rvalue, "%");
938         if (e) {
939                 unsigned long ul;
940                 char *f;
941
942                 errno = 0;
943                 ul = strtoul(rvalue, &f, 10);
944                 if (errno != 0 || f != e) {
945                         log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
946                         return 0;
947                 }
948
949                 if (ul <= 0 || ul >= 100) {
950                         log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
951                         return 0;
952                 }
953
954                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
955         } else {
956                 uint64_t k;
957
958                 r = parse_size(rvalue, 1024, &k);
959                 if (r < 0 || (uint64_t) (size_t) k != k) {
960                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
961                         return 0;
962                 }
963
964                 *sz = PAGE_ALIGN((size_t) k);
965         }
966
967         return 0;
968 }