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