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