]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
util-lib: split string parsing related calls from util.[ch] into parse-util.[ch]
[thirdparty/systemd.git] / src / login / logind-user.c
CommitLineData
20263082
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
20263082
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
20263082 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
20263082
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
4f5dd394 22#include <errno.h>
20263082 23#include <string.h>
4f5dd394 24#include <sys/mount.h>
20263082 25#include <unistd.h>
20263082 26
cc377381 27#include "bus-error.h"
4f5dd394 28#include "bus-util.h"
66cdd0f2 29#include "clean-ipc.h"
4f5dd394
LP
30#include "conf-parser.h"
31#include "escape.h"
3ffd4af2 32#include "fd-util.h"
4f5dd394 33#include "fileio.h"
6482f626 34#include "formats-util.h"
4f5dd394 35#include "hashmap.h"
9e281beb 36#include "label.h"
3ffd4af2 37#include "logind-user.h"
4f5dd394 38#include "mkdir.h"
6bedfcbb 39#include "parse-util.h"
4f5dd394
LP
40#include "path-util.h"
41#include "rm-rf.h"
42#include "smack-util.h"
43#include "special.h"
44#include "unit-name.h"
45#include "util.h"
20263082
LP
46
47User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
48 User *u;
49
50 assert(m);
51 assert(name);
52
14c3baca 53 u = new0(User, 1);
20263082
LP
54 if (!u)
55 return NULL;
56
57 u->name = strdup(name);
9444b1f2
LP
58 if (!u->name)
59 goto fail;
20263082 60
de0671ee 61 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
9444b1f2 62 goto fail;
20263082 63
8cb4ab00 64 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
9444b1f2 65 goto fail;
20263082
LP
66
67 u->manager = m;
68 u->uid = uid;
69 u->gid = gid;
70
71 return u;
9444b1f2
LP
72
73fail:
74 free(u->state_file);
75 free(u->name);
76 free(u);
77
78 return NULL;
20263082
LP
79}
80
81void user_free(User *u) {
82 assert(u);
83
14c3baca 84 if (u->in_gc_queue)
71fda00f 85 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
14c3baca 86
20263082
LP
87 while (u->sessions)
88 session_free(u->sessions);
89
fb6becb4
LP
90 if (u->slice) {
91 hashmap_remove(u->manager->user_units, u->slice);
92 free(u->slice);
9444b1f2 93 }
20263082 94
fb6becb4
LP
95 if (u->service) {
96 hashmap_remove(u->manager->user_units, u->service);
97 free(u->service);
98 }
99
100 free(u->slice_job);
101 free(u->service_job);
102
20263082
LP
103 free(u->runtime_path);
104
8cb4ab00 105 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
20263082
LP
106
107 free(u->name);
d2f92cdf 108 free(u->state_file);
20263082
LP
109 free(u);
110}
111
71161305 112static int user_save_internal(User *u) {
9444b1f2
LP
113 _cleanup_free_ char *temp_path = NULL;
114 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
115 int r;
116
117 assert(u);
118 assert(u->state_file);
119
d2e54fae 120 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 121 if (r < 0)
dacd6cee 122 goto fail;
20263082 123
14c3baca
LP
124 r = fopen_temporary(u->state_file, &f, &temp_path);
125 if (r < 0)
dacd6cee 126 goto fail;
14c3baca
LP
127
128 fchmod(fileno(f), 0644);
20263082
LP
129
130 fprintf(f,
14c3baca 131 "# This is private data. Do not parse.\n"
20263082
LP
132 "NAME=%s\n"
133 "STATE=%s\n",
134 u->name,
135 user_state_to_string(user_get_state(u)));
136
20263082 137 if (u->runtime_path)
9444b1f2 138 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
20263082
LP
139
140 if (u->service)
9444b1f2 141 fprintf(f, "SERVICE=%s\n", u->service);
fb6becb4
LP
142 if (u->service_job)
143 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
9444b1f2
LP
144
145 if (u->slice)
146 fprintf(f, "SLICE=%s\n", u->slice);
fb6becb4
LP
147 if (u->slice_job)
148 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
20263082
LP
149
150 if (u->display)
9444b1f2
LP
151 fprintf(f, "DISPLAY=%s\n", u->display->id);
152
153 if (dual_timestamp_is_set(&u->timestamp))
20263082 154 fprintf(f,
90b2de37
ZJS
155 "REALTIME="USEC_FMT"\n"
156 "MONOTONIC="USEC_FMT"\n",
157 u->timestamp.realtime,
158 u->timestamp.monotonic);
20263082 159
034a2a52
LP
160 if (u->sessions) {
161 Session *i;
9b958eff 162 bool first;
034a2a52
LP
163
164 fputs("SESSIONS=", f);
9b958eff 165 first = true;
034a2a52 166 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
167 if (first)
168 first = false;
169 else
170 fputc(' ', f);
171
172 fputs(i->id, f);
034a2a52
LP
173 }
174
9b958eff
LP
175 fputs("\nSEATS=", f);
176 first = true;
034a2a52 177 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
178 if (!i->seat)
179 continue;
180
181 if (first)
182 first = false;
183 else
184 fputc(' ', f);
185
186 fputs(i->seat->id, f);
034a2a52
LP
187 }
188
9b958eff
LP
189 fputs("\nACTIVE_SESSIONS=", f);
190 first = true;
191 LIST_FOREACH(sessions_by_user, i, u->sessions) {
192 if (!session_is_active(i))
193 continue;
194
195 if (first)
196 first = false;
197 else
198 fputc(' ', f);
199
200 fputs(i->id, f);
201 }
034a2a52 202
2dc8f41a
CG
203 fputs("\nONLINE_SESSIONS=", f);
204 first = true;
205 LIST_FOREACH(sessions_by_user, i, u->sessions) {
206 if (session_get_state(i) == SESSION_CLOSING)
207 continue;
208
209 if (first)
210 first = false;
211 else
212 fputc(' ', f);
213
214 fputs(i->id, f);
215 }
216
9b958eff
LP
217 fputs("\nACTIVE_SEATS=", f);
218 first = true;
034a2a52 219 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
220 if (!session_is_active(i) || !i->seat)
221 continue;
222
223 if (first)
224 first = false;
225 else
47acb2f1
CG
226 fputc(' ', f);
227
228 fputs(i->seat->id, f);
034a2a52 229 }
2dc8f41a
CG
230
231 fputs("\nONLINE_SEATS=", f);
232 first = true;
233 LIST_FOREACH(sessions_by_user, i, u->sessions) {
234 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
235 continue;
236
237 if (first)
238 first = false;
239 else
240 fputc(' ', f);
241
242 fputs(i->seat->id, f);
243 }
9b958eff 244 fputc('\n', f);
034a2a52
LP
245 }
246
dacd6cee
LP
247 r = fflush_and_check(f);
248 if (r < 0)
249 goto fail;
14c3baca 250
dacd6cee 251 if (rename(temp_path, u->state_file) < 0) {
20263082 252 r = -errno;
dacd6cee 253 goto fail;
20263082
LP
254 }
255
dacd6cee
LP
256 return 0;
257
258fail:
259 (void) unlink(u->state_file);
260
261 if (temp_path)
262 (void) unlink(temp_path);
14c3baca 263
dacd6cee 264 return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
20263082
LP
265}
266
71161305
SM
267int user_save(User *u) {
268 assert(u);
269
270 if (!u->started)
271 return 0;
272
273 return user_save_internal (u);
274}
275
20263082 276int user_load(User *u) {
9444b1f2 277 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
98a28fef 278 Session *s = NULL;
9444b1f2 279 int r;
20263082
LP
280
281 assert(u);
282
a185c5aa 283 r = parse_env_file(u->state_file, NEWLINE,
fb6becb4
LP
284 "RUNTIME", &u->runtime_path,
285 "SERVICE", &u->service,
286 "SERVICE_JOB", &u->service_job,
287 "SLICE", &u->slice,
288 "SLICE_JOB", &u->slice_job,
289 "DISPLAY", &display,
290 "REALTIME", &realtime,
291 "MONOTONIC", &monotonic,
20263082
LP
292 NULL);
293 if (r < 0) {
20263082
LP
294 if (r == -ENOENT)
295 return 0;
296
da927ba9 297 log_error_errno(r, "Failed to read %s: %m", u->state_file);
20263082
LP
298 return r;
299 }
300
9444b1f2 301 if (display)
98a28fef 302 s = hashmap_get(u->manager->sessions, display);
20263082 303
4d6d6518 304 if (s && s->display && display_is_local(s->display))
20263082
LP
305 u->display = s;
306
9444b1f2
LP
307 if (realtime) {
308 unsigned long long l;
309 if (sscanf(realtime, "%llu", &l) > 0)
310 u->timestamp.realtime = l;
311 }
312
313 if (monotonic) {
314 unsigned long long l;
315 if (sscanf(monotonic, "%llu", &l) > 0)
316 u->timestamp.monotonic = l;
317 }
318
20263082
LP
319 return r;
320}
321
322static int user_mkdir_runtime_path(User *u) {
323 char *p;
324 int r;
325
326 assert(u);
327
d2e54fae 328 r = mkdir_safe_label("/run/user", 0755, 0, 0);
f647962d
MS
329 if (r < 0)
330 return log_error_errno(r, "Failed to create /run/user: %m");
20263082
LP
331
332 if (!u->runtime_path) {
1c231f56 333 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
0d0f0c50 334 return log_oom();
20263082
LP
335 } else
336 p = u->runtime_path;
337
e26d6ce5 338 if (path_is_mount_point(p, 0) <= 0) {
1c231f56
LP
339 _cleanup_free_ char *t = NULL;
340
9e281beb 341 (void) mkdir_label(p, 0700);
1c231f56 342
6baa7db0 343 if (mac_smack_use())
374738d5
LS
344 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
345 else
346 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
374738d5 347 if (r < 0) {
1c231f56
LP
348 r = log_oom();
349 goto fail;
350 }
351
352 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
353 if (r < 0) {
11c6476a
CS
354 if (errno != EPERM) {
355 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
356 goto fail;
357 }
358
359 /* Lacking permissions, maybe
360 * CAP_SYS_ADMIN-less container? In this case,
361 * just use a normal directory. */
362
363 r = chmod_and_chown(p, 0700, u->uid, u->gid);
364 if (r < 0) {
365 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
366 goto fail;
367 }
1c231f56 368 }
9e281beb
LP
369
370 r = label_fix(p, false, false);
371 if (r < 0)
372 log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
20263082
LP
373 }
374
375 u->runtime_path = p;
376 return 0;
1c231f56
LP
377
378fail:
4d858e7d
CS
379 if (p) {
380 /* Try to clean up, but ignore errors */
381 (void) rmdir(p);
382 free(p);
383 }
384
1c231f56
LP
385 u->runtime_path = NULL;
386 return r;
20263082
LP
387}
388
fb6becb4 389static int user_start_slice(User *u) {
fb6becb4 390 char *job;
20263082
LP
391 int r;
392
393 assert(u);
394
fb6becb4 395 if (!u->slice) {
cc377381 396 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de0671ee
ZJS
397 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
398 sprintf(lu, UID_FMT, u->uid);
ae018d9b 399
7410616c 400 r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
9444b1f2
LP
401 if (r < 0)
402 return r;
403
d0af76e6
LP
404 r = manager_start_unit(u->manager, slice, &error, &job);
405 if (r < 0) {
cc377381 406 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
d0af76e6
LP
407 free(slice);
408 } else {
409 u->slice = slice;
410
411 free(u->slice_job);
412 u->slice_job = job;
413 }
20263082
LP
414 }
415
d0af76e6
LP
416 if (u->slice)
417 hashmap_put(u->manager->user_units, u->slice, u);
418
20263082
LP
419 return 0;
420}
421
422static int user_start_service(User *u) {
cc377381 423 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
424 char *job;
425 int r;
426
20263082
LP
427 assert(u);
428
fb6becb4 429 if (!u->service) {
de0671ee
ZJS
430 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
431 sprintf(lu, UID_FMT, u->uid);
fb6becb4 432
7410616c
LP
433 r = unit_name_build("user", lu, ".service", &service);
434 if (r < 0)
435 return log_error_errno(r, "Failed to build service name: %m");
fb6becb4 436
d0af76e6
LP
437 r = manager_start_unit(u->manager, service, &error, &job);
438 if (r < 0) {
cc377381 439 log_error("Failed to start user service: %s", bus_error_message(&error, r));
d0af76e6
LP
440 free(service);
441 } else {
442 u->service = service;
443
444 free(u->service_job);
445 u->service_job = job;
446 }
fb6becb4
LP
447 }
448
d0af76e6
LP
449 if (u->service)
450 hashmap_put(u->manager->user_units, u->service, u);
451
20263082
LP
452 return 0;
453}
454
455int user_start(User *u) {
456 int r;
457
458 assert(u);
459
9418f147
LP
460 if (u->started)
461 return 0;
462
1637a8be 463 log_debug("New user %s logged in.", u->name);
ed18b08b 464
20263082
LP
465 /* Make XDG_RUNTIME_DIR */
466 r = user_mkdir_runtime_path(u);
467 if (r < 0)
468 return r;
469
470 /* Create cgroup */
fb6becb4 471 r = user_start_slice(u);
20263082
LP
472 if (r < 0)
473 return r;
474
71161305
SM
475 /* Save the user data so far, because pam_systemd will read the
476 * XDG_RUNTIME_DIR out of it while starting up systemd --user.
477 * We need to do user_save_internal() because we have not
478 * "officially" started yet. */
479 user_save_internal(u);
480
20263082
LP
481 /* Spawn user systemd */
482 r = user_start_service(u);
483 if (r < 0)
484 return r;
485
9444b1f2
LP
486 if (!dual_timestamp_is_set(&u->timestamp))
487 dual_timestamp_get(&u->timestamp);
20263082 488
9418f147
LP
489 u->started = true;
490
7f7bb946
LP
491 /* Save new user data */
492 user_save(u);
493
da119395
LP
494 user_send_signal(u, true);
495
20263082
LP
496 return 0;
497}
498
fb6becb4 499static int user_stop_slice(User *u) {
cc377381 500 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
501 char *job;
502 int r;
20263082 503
20263082
LP
504 assert(u);
505
fb6becb4
LP
506 if (!u->slice)
507 return 0;
508
509 r = manager_stop_unit(u->manager, u->slice, &error, &job);
510 if (r < 0) {
cc377381 511 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
512 return r;
513 }
ed18b08b 514
fb6becb4
LP
515 free(u->slice_job);
516 u->slice_job = job;
ed18b08b 517
fb6becb4 518 return r;
20263082
LP
519}
520
fb6becb4 521static int user_stop_service(User *u) {
cc377381 522 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 523 char *job;
20263082 524 int r;
20263082
LP
525
526 assert(u);
527
fb6becb4 528 if (!u->service)
20263082
LP
529 return 0;
530
fb6becb4
LP
531 r = manager_stop_unit(u->manager, u->service, &error, &job);
532 if (r < 0) {
cc377381 533 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
fb6becb4
LP
534 return r;
535 }
20263082 536
fb6becb4
LP
537 free(u->service_job);
538 u->service_job = job;
20263082 539
fb6becb4
LP
540 return r;
541}
20263082 542
20263082
LP
543static int user_remove_runtime_path(User *u) {
544 int r;
545
546 assert(u);
547
548 if (!u->runtime_path)
549 return 0;
550
c6878637 551 r = rm_rf(u->runtime_path, 0);
1c231f56 552 if (r < 0)
da927ba9 553 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
1c231f56 554
11c6476a
CS
555 /* Ignore cases where the directory isn't mounted, as that's
556 * quite possible, if we lacked the permissions to mount
557 * something */
558 r = umount2(u->runtime_path, MNT_DETACH);
559 if (r < 0 && errno != EINVAL && errno != ENOENT)
56f64d95 560 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
1c231f56 561
c6878637 562 r = rm_rf(u->runtime_path, REMOVE_ROOT);
20263082 563 if (r < 0)
da927ba9 564 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
20263082 565
a1e58e8e 566 u->runtime_path = mfree(u->runtime_path);
20263082
LP
567
568 return r;
569}
570
9bb69af4 571int user_stop(User *u, bool force) {
20263082
LP
572 Session *s;
573 int r = 0, k;
574 assert(u);
575
b58b227a
DH
576 /* Stop jobs have already been queued */
577 if (u->stopping) {
578 user_save(u);
579 return r;
580 }
581
20263082 582 LIST_FOREACH(sessions_by_user, s, u->sessions) {
9bb69af4 583 k = session_stop(s, force);
20263082
LP
584 if (k < 0)
585 r = k;
586 }
587
588 /* Kill systemd */
589 k = user_stop_service(u);
590 if (k < 0)
591 r = k;
592
593 /* Kill cgroup */
fb6becb4 594 k = user_stop_slice(u);
20263082
LP
595 if (k < 0)
596 r = k;
597
5f41d1f1
LP
598 u->stopping = true;
599
405e0255
LP
600 user_save(u);
601
602 return r;
603}
604
605int user_finalize(User *u) {
606 Session *s;
607 int r = 0, k;
608
609 assert(u);
610
611 if (u->started)
612 log_debug("User %s logged out.", u->name);
613
614 LIST_FOREACH(sessions_by_user, s, u->sessions) {
615 k = session_finalize(s);
616 if (k < 0)
617 r = k;
618 }
619
20263082
LP
620 /* Kill XDG_RUNTIME_DIR */
621 k = user_remove_runtime_path(u);
622 if (k < 0)
623 r = k;
624
66cdd0f2
LP
625 /* Clean SysV + POSIX IPC objects */
626 if (u->manager->remove_ipc) {
627 k = clean_ipc(u->uid);
628 if (k < 0)
629 r = k;
630 }
631
d2f92cdf
LP
632 unlink(u->state_file);
633 user_add_to_gc_queue(u);
634
405e0255 635 if (u->started) {
ed18b08b 636 user_send_signal(u, false);
405e0255
LP
637 u->started = false;
638 }
9418f147 639
20263082
LP
640 return r;
641}
642
a185c5aa
LP
643int user_get_idle_hint(User *u, dual_timestamp *t) {
644 Session *s;
645 bool idle_hint = true;
5cb14b37 646 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
a185c5aa
LP
647
648 assert(u);
649
650 LIST_FOREACH(sessions_by_user, s, u->sessions) {
651 dual_timestamp k;
652 int ih;
653
654 ih = session_get_idle_hint(s, &k);
655 if (ih < 0)
656 return ih;
657
658 if (!ih) {
659 if (!idle_hint) {
660 if (k.monotonic < ts.monotonic)
661 ts = k;
662 } else {
663 idle_hint = false;
664 ts = k;
665 }
666 } else if (idle_hint) {
667
668 if (k.monotonic > ts.monotonic)
669 ts = k;
670 }
671 }
672
673 if (t)
674 *t = ts;
675
676 return idle_hint;
677}
678
3a9f7a30 679int user_check_linger_file(User *u) {
cc377381
LP
680 _cleanup_free_ char *cc = NULL;
681 char *p = NULL;
e96cd586 682
cc377381
LP
683 cc = cescape(u->name);
684 if (!cc)
e96cd586
LP
685 return -ENOMEM;
686
63c372cb 687 p = strjoina("/var/lib/systemd/linger/", cc);
e96cd586 688
cc377381 689 return access(p, F_OK) >= 0;
e96cd586
LP
690}
691
cc377381 692bool user_check_gc(User *u, bool drop_not_started) {
20263082
LP
693 assert(u);
694
4a4b033f 695 if (drop_not_started && !u->started)
cc377381 696 return false;
932e3ee7 697
20263082 698 if (u->sessions)
cc377381 699 return true;
20263082 700
e96cd586 701 if (user_check_linger_file(u) > 0)
cc377381 702 return true;
20263082 703
cc377381
LP
704 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
705 return true;
706
707 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
708 return true;
405e0255 709
cc377381 710 return false;
20263082
LP
711}
712
14c3baca
LP
713void user_add_to_gc_queue(User *u) {
714 assert(u);
715
716 if (u->in_gc_queue)
717 return;
718
71fda00f 719 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
14c3baca
LP
720 u->in_gc_queue = true;
721}
722
20263082
LP
723UserState user_get_state(User *u) {
724 Session *i;
725
726 assert(u);
727
5f41d1f1
LP
728 if (u->stopping)
729 return USER_CLOSING;
730
71161305 731 if (!u->started || u->slice_job || u->service_job)
405e0255 732 return USER_OPENING;
c9caad80 733
5f41d1f1
LP
734 if (u->sessions) {
735 bool all_closing = true;
736
737 LIST_FOREACH(sessions_by_user, i, u->sessions) {
00555a2e
DH
738 SessionState state;
739
740 state = session_get_state(i);
741 if (state == SESSION_ACTIVE)
5f41d1f1 742 return USER_ACTIVE;
00555a2e 743 if (state != SESSION_CLOSING)
5f41d1f1
LP
744 all_closing = false;
745 }
20263082 746
c9caad80 747 return all_closing ? USER_CLOSING : USER_ONLINE;
5f41d1f1 748 }
e96cd586
LP
749
750 if (user_check_linger_file(u) > 0)
751 return USER_LINGERING;
752
753 return USER_CLOSING;
20263082
LP
754}
755
de07ab16 756int user_kill(User *u, int signo) {
de07ab16
LP
757 assert(u);
758
fb6becb4 759 if (!u->slice)
de07ab16
LP
760 return -ESRCH;
761
fb6becb4 762 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
de07ab16
LP
763}
764
cde40acc 765static bool elect_display_filter(Session *s) {
7ffeb45c
PW
766 /* Return true if the session is a candidate for the user’s ‘primary
767 * session’ or ‘display’. */
768 assert(s);
769
770 return (s->class == SESSION_USER && !s->stopping);
771}
772
cde40acc 773static int elect_display_compare(Session *s1, Session *s2) {
7ffeb45c
PW
774 /* Indexed by SessionType. Lower numbers mean more preferred. */
775 const int type_ranks[_SESSION_TYPE_MAX] = {
776 [SESSION_UNSPECIFIED] = 0,
777 [SESSION_TTY] = -2,
778 [SESSION_X11] = -3,
779 [SESSION_WAYLAND] = -3,
780 [SESSION_MIR] = -3,
781 [SESSION_WEB] = -1,
782 };
783
784 /* Calculate the partial order relationship between s1 and s2,
785 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
786 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
787 * is preferred.
788 *
789 * s1 or s2 may be NULL. */
b9460fdc
RC
790 if (!s1 && !s2)
791 return 0;
792
7ffeb45c
PW
793 if ((s1 == NULL) != (s2 == NULL))
794 return (s1 == NULL) - (s2 == NULL);
795
796 if (s1->stopping != s2->stopping)
797 return s1->stopping - s2->stopping;
798
799 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
800 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
801
802 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
803 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
804
805 if (s1->type != s2->type)
806 return type_ranks[s1->type] - type_ranks[s2->type];
807
808 return 0;
809}
810
952d3260 811void user_elect_display(User *u) {
7ffeb45c 812 Session *s;
952d3260
LP
813
814 assert(u);
815
816 /* This elects a primary session for each user, which we call
817 * the "display". We try to keep the assignment stable, but we
818 * "upgrade" to better choices. */
7ffeb45c 819 log_debug("Electing new display for user %s", u->name);
952d3260
LP
820
821 LIST_FOREACH(sessions_by_user, s, u->sessions) {
7ffeb45c
PW
822 if (!elect_display_filter(s)) {
823 log_debug("Ignoring session %s", s->id);
952d3260 824 continue;
7ffeb45c 825 }
952d3260 826
7ffeb45c
PW
827 if (elect_display_compare(s, u->display) < 0) {
828 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
829 u->display = s;
830 }
e9e74f28 831 }
952d3260
LP
832}
833
20263082
LP
834static const char* const user_state_table[_USER_STATE_MAX] = {
835 [USER_OFFLINE] = "offline",
fb6becb4 836 [USER_OPENING] = "opening",
20263082
LP
837 [USER_LINGERING] = "lingering",
838 [USER_ONLINE] = "online",
e96cd586
LP
839 [USER_ACTIVE] = "active",
840 [USER_CLOSING] = "closing"
20263082
LP
841};
842
843DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
1c231f56
LP
844
845int config_parse_tmpfs_size(
846 const char* unit,
847 const char *filename,
848 unsigned line,
849 const char *section,
850 unsigned section_line,
851 const char *lvalue,
852 int ltype,
853 const char *rvalue,
854 void *data,
855 void *userdata) {
856
857 size_t *sz = data;
858 const char *e;
859 int r;
860
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
865
866 e = endswith(rvalue, "%");
867 if (e) {
868 unsigned long ul;
869 char *f;
870
871 errno = 0;
872 ul = strtoul(rvalue, &f, 10);
873 if (errno != 0 || f != e) {
12ca818f 874 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
1c231f56
LP
875 return 0;
876 }
877
878 if (ul <= 0 || ul >= 100) {
12ca818f 879 log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
1c231f56
LP
880 return 0;
881 }
882
883 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
884 } else {
59f448cf 885 uint64_t k;
1c231f56 886
59f448cf
LP
887 r = parse_size(rvalue, 1024, &k);
888 if (r < 0 || (uint64_t) (size_t) k != k) {
12ca818f 889 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
1c231f56
LP
890 return 0;
891 }
892
59f448cf 893 *sz = PAGE_ALIGN((size_t) k);
1c231f56
LP
894 }
895
896 return 0;
897}