chiark / gitweb /
logind: fix misleading message
[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(u->service_job);
553         u->service_job = job;
554
555         return r;
556 }
557 #endif // 0
558
559 static int user_remove_runtime_path(User *u) {
560         int r;
561
562         assert(u);
563
564         r = rm_rf(u->runtime_path, 0);
565         if (r < 0)
566                 log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", u->runtime_path);
567
568         /* Ignore cases where the directory isn't mounted, as that's
569          * quite possible, if we lacked the permissions to mount
570          * something */
571         r = umount2(u->runtime_path, MNT_DETACH);
572         if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
573                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
574
575         r = rm_rf(u->runtime_path, REMOVE_ROOT);
576         if (r < 0)
577                 log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", u->runtime_path);
578
579         return r;
580 }
581
582 int user_stop(User *u, bool force) {
583         Session *s;
584         int r = 0, k;
585         assert(u);
586
587         /* Stop jobs have already been queued */
588         if (u->stopping) {
589                 user_save(u);
590                 return r;
591         }
592
593         LIST_FOREACH(sessions_by_user, s, u->sessions) {
594                 k = session_stop(s, force);
595                 if (k < 0)
596                         r = k;
597         }
598
599         /* Kill systemd */
600 #if 0 /// elogind does not support service or slice jobs
601         k = user_stop_service(u);
602         if (k < 0)
603                 r = k;
604
605         /* Kill cgroup */
606         k = user_stop_slice(u);
607         if (k < 0)
608                 r = k;
609 #endif // 0
610
611         u->stopping = true;
612
613         user_save(u);
614
615 #if 1 /// elogind must queue this user again
616         user_add_to_gc_queue(u);
617 #endif // 1
618         return r;
619 }
620
621 int user_finalize(User *u) {
622         Session *s;
623         int r = 0, k;
624
625         assert(u);
626
627         if (u->started)
628                 log_debug("User %s logged out.", u->name);
629
630         LIST_FOREACH(sessions_by_user, s, u->sessions) {
631                 k = session_finalize(s);
632                 if (k < 0)
633                         r = k;
634         }
635
636         /* Kill XDG_RUNTIME_DIR */
637         k = user_remove_runtime_path(u);
638         if (k < 0)
639                 r = k;
640
641         /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
642          * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
643          * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
644          * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
645          * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
646          * and do it only for normal users. */
647         if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
648                 k = clean_ipc_by_uid(u->uid);
649                 if (k < 0)
650                         r = k;
651         }
652
653         unlink(u->state_file);
654         user_add_to_gc_queue(u);
655
656         if (u->started) {
657                 user_send_signal(u, false);
658                 u->started = false;
659         }
660
661         return r;
662 }
663
664 int user_get_idle_hint(User *u, dual_timestamp *t) {
665         Session *s;
666         bool idle_hint = true;
667         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
668
669         assert(u);
670
671         LIST_FOREACH(sessions_by_user, s, u->sessions) {
672                 dual_timestamp k;
673                 int ih;
674
675                 ih = session_get_idle_hint(s, &k);
676                 if (ih < 0)
677                         return ih;
678
679                 if (!ih) {
680                         if (!idle_hint) {
681                                 if (k.monotonic < ts.monotonic)
682                                         ts = k;
683                         } else {
684                                 idle_hint = false;
685                                 ts = k;
686                         }
687                 } else if (idle_hint) {
688
689                         if (k.monotonic > ts.monotonic)
690                                 ts = k;
691                 }
692         }
693
694         if (t)
695                 *t = ts;
696
697         return idle_hint;
698 }
699
700 int user_check_linger_file(User *u) {
701         _cleanup_free_ char *cc = NULL;
702         char *p = NULL;
703
704         cc = cescape(u->name);
705         if (!cc)
706                 return -ENOMEM;
707
708         p = strjoina("/var/lib/elogind/linger/", cc);
709
710         return access(p, F_OK) >= 0;
711 }
712
713 bool user_check_gc(User *u, bool drop_not_started) {
714         assert(u);
715
716         if (drop_not_started && !u->started)
717                 return false;
718
719         if (u->sessions)
720                 return true;
721
722         if (user_check_linger_file(u) > 0)
723                 return true;
724
725 #if 0 /// elogind neither supports service nor slice jobs
726         if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
727                 return true;
728
729         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
730                 return true;
731 #endif // 0
732
733         return false;
734 }
735
736 void user_add_to_gc_queue(User *u) {
737         assert(u);
738
739         if (u->in_gc_queue)
740                 return;
741
742         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
743         u->in_gc_queue = true;
744 }
745
746 UserState user_get_state(User *u) {
747         Session *i;
748
749         assert(u);
750
751         if (u->stopping)
752                 return USER_CLOSING;
753
754 #if 0 /// elogind neither supports service nor slice jobs.
755         if (!u->started || u->slice_job || u->service_job)
756 #else
757         if (!u->started)
758 #endif // 0
759                 return USER_OPENING;
760
761         if (u->sessions) {
762                 bool all_closing = true;
763
764                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
765                         SessionState state;
766
767                         state = session_get_state(i);
768                         if (state == SESSION_ACTIVE)
769                                 return USER_ACTIVE;
770                         if (state != SESSION_CLOSING)
771                                 all_closing = false;
772                 }
773
774                 return all_closing ? USER_CLOSING : USER_ONLINE;
775         }
776
777         if (user_check_linger_file(u) > 0)
778                 return USER_LINGERING;
779
780         return USER_CLOSING;
781 }
782
783 int user_kill(User *u, int signo) {
784 #if 0 /// Without systemd unit support, elogind has to rely on its session system
785         assert(u);
786
787         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
788 #else
789         Session *s;
790         int res = 0;
791
792         assert(u);
793
794         LIST_FOREACH(sessions_by_user, s, u->sessions) {
795                 int r = session_kill(s, KILL_ALL, signo);
796                 if (res == 0 && r < 0)
797                         res = r;
798         }
799
800         return res;
801 #endif // 0
802 }
803
804 static bool elect_display_filter(Session *s) {
805         /* Return true if the session is a candidate for the user’s ‘primary
806          * session’ or ‘display’. */
807         assert(s);
808
809         return (s->class == SESSION_USER && !s->stopping);
810 }
811
812 static int elect_display_compare(Session *s1, Session *s2) {
813         /* Indexed by SessionType. Lower numbers mean more preferred. */
814         const int type_ranks[_SESSION_TYPE_MAX] = {
815                 [SESSION_UNSPECIFIED] = 0,
816                 [SESSION_TTY] = -2,
817                 [SESSION_X11] = -3,
818                 [SESSION_WAYLAND] = -3,
819                 [SESSION_MIR] = -3,
820                 [SESSION_WEB] = -1,
821         };
822
823         /* Calculate the partial order relationship between s1 and s2,
824          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
825          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
826          * is preferred.
827          *
828          * s1 or s2 may be NULL. */
829         if (!s1 && !s2)
830                 return 0;
831
832         if ((s1 == NULL) != (s2 == NULL))
833                 return (s1 == NULL) - (s2 == NULL);
834
835         if (s1->stopping != s2->stopping)
836                 return s1->stopping - s2->stopping;
837
838         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
839                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
840
841         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
842                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
843
844         if (s1->type != s2->type)
845                 return type_ranks[s1->type] - type_ranks[s2->type];
846
847         return 0;
848 }
849
850 void user_elect_display(User *u) {
851         Session *s;
852
853         assert(u);
854
855         /* This elects a primary session for each user, which we call
856          * the "display". We try to keep the assignment stable, but we
857          * "upgrade" to better choices. */
858         log_debug("Electing new display for user %s", u->name);
859
860         LIST_FOREACH(sessions_by_user, s, u->sessions) {
861                 if (!elect_display_filter(s)) {
862                         log_debug("Ignoring session %s", s->id);
863                         continue;
864                 }
865
866                 if (elect_display_compare(s, u->display) < 0) {
867                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
868                         u->display = s;
869                 }
870         }
871 }
872
873 static const char* const user_state_table[_USER_STATE_MAX] = {
874         [USER_OFFLINE] = "offline",
875         [USER_OPENING] = "opening",
876         [USER_LINGERING] = "lingering",
877         [USER_ONLINE] = "online",
878         [USER_ACTIVE] = "active",
879         [USER_CLOSING] = "closing"
880 };
881
882 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
883
884 int config_parse_tmpfs_size(
885                 const char* unit,
886                 const char *filename,
887                 unsigned line,
888                 const char *section,
889                 unsigned section_line,
890                 const char *lvalue,
891                 int ltype,
892                 const char *rvalue,
893                 void *data,
894                 void *userdata) {
895
896         size_t *sz = data;
897         int r;
898
899         assert(filename);
900         assert(lvalue);
901         assert(rvalue);
902         assert(data);
903
904         /* First, try to parse as percentage */
905         r = parse_percent(rvalue);
906         if (r > 0 && r < 100)
907                 *sz = physical_memory_scale(r, 100U);
908         else {
909                 uint64_t k;
910
911                 /* If the passed argument was not a percentage, or out of range, parse as byte size */
912
913                 r = parse_size(rvalue, 1024, &k);
914                 if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
915                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
916                         return 0;
917                 }
918
919                 *sz = PAGE_ALIGN((size_t) k);
920         }
921
922         return 0;
923 }
924
925 int config_parse_user_tasks_max(
926                 const char* unit,
927                 const char *filename,
928                 unsigned line,
929                 const char *section,
930                 unsigned section_line,
931                 const char *lvalue,
932                 int ltype,
933                 const char *rvalue,
934                 void *data,
935                 void *userdata) {
936
937         uint64_t *m = data;
938         uint64_t k;
939         int r;
940
941         assert(filename);
942         assert(lvalue);
943         assert(rvalue);
944         assert(data);
945
946         if (isempty(rvalue)) {
947                 *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
948                 return 0;
949         }
950
951         if (streq(rvalue, "infinity")) {
952                 *m = CGROUP_LIMIT_MAX;
953                 return 0;
954         }
955
956         /* Try to parse as percentage */
957         r = parse_percent(rvalue);
958         if (r >= 0)
959                 k = system_tasks_max_scale(r, 100U);
960         else {
961
962                 /* If the passed argument was not a percentage, or out of range, parse as byte size */
963
964                 r = safe_atou64(rvalue, &k);
965                 if (r < 0) {
966                         log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue);
967                         return 0;
968                 }
969         }
970
971         if (k <= 0 || k >= UINT64_MAX) {
972                 log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue);
973                 return 0;
974         }
975
976         *m = k;
977         return 0;
978 }