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