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