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