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