]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
login: keep user->slice constant
[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
LP
53
54User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
6230bf75 55 char lu[DECIMAL_STR_MAX(uid_t) + 1];
20263082 56 User *u;
6230bf75 57 int r;
20263082
LP
58
59 assert(m);
60 assert(name);
61
14c3baca 62 u = new0(User, 1);
20263082
LP
63 if (!u)
64 return NULL;
65
66 u->name = strdup(name);
9444b1f2
LP
67 if (!u->name)
68 goto fail;
20263082 69
de0671ee 70 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
9444b1f2 71 goto fail;
20263082 72
f9e4283d
DH
73 if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
74 goto fail;
75
6230bf75
DH
76 sprintf(lu, UID_FMT, uid);
77 r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
78 if (r < 0)
79 goto fail;
80
8cb4ab00 81 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
9444b1f2 82 goto fail;
20263082 83
6230bf75
DH
84 if (hashmap_put(m->user_units, u->slice, u) < 0)
85 goto fail;
86
20263082
LP
87 u->manager = m;
88 u->uid = uid;
89 u->gid = gid;
90
91 return u;
9444b1f2
LP
92
93fail:
6230bf75
DH
94 if (u->slice)
95 hashmap_remove(m->user_units, u->slice);
96 hashmap_remove(m->users, UID_TO_PTR(uid));
97 free(u->slice);
f9e4283d 98 free(u->runtime_path);
9444b1f2
LP
99 free(u->state_file);
100 free(u->name);
101 free(u);
102
103 return NULL;
20263082
LP
104}
105
106void user_free(User *u) {
107 assert(u);
108
14c3baca 109 if (u->in_gc_queue)
71fda00f 110 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
14c3baca 111
20263082
LP
112 while (u->sessions)
113 session_free(u->sessions);
114
fb6becb4
LP
115 if (u->service) {
116 hashmap_remove(u->manager->user_units, u->service);
117 free(u->service);
118 }
119
6230bf75
DH
120 hashmap_remove(u->manager->user_units, u->slice);
121 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
122
fb6becb4
LP
123 free(u->slice_job);
124 free(u->service_job);
125
6230bf75 126 free(u->slice);
20263082
LP
127 free(u->runtime_path);
128
20263082 129 free(u->name);
d2f92cdf 130 free(u->state_file);
20263082
LP
131 free(u);
132}
133
71161305 134static int user_save_internal(User *u) {
9444b1f2
LP
135 _cleanup_free_ char *temp_path = NULL;
136 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
137 int r;
138
139 assert(u);
140 assert(u->state_file);
141
d2e54fae 142 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 143 if (r < 0)
dacd6cee 144 goto fail;
20263082 145
14c3baca
LP
146 r = fopen_temporary(u->state_file, &f, &temp_path);
147 if (r < 0)
dacd6cee 148 goto fail;
14c3baca
LP
149
150 fchmod(fileno(f), 0644);
20263082
LP
151
152 fprintf(f,
14c3baca 153 "# This is private data. Do not parse.\n"
20263082
LP
154 "NAME=%s\n"
155 "STATE=%s\n",
156 u->name,
157 user_state_to_string(user_get_state(u)));
158
f9e4283d 159 /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
20263082 160 if (u->runtime_path)
9444b1f2 161 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
20263082
LP
162
163 if (u->service)
9444b1f2 164 fprintf(f, "SERVICE=%s\n", u->service);
fb6becb4
LP
165 if (u->service_job)
166 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
9444b1f2 167
fb6becb4
LP
168 if (u->slice_job)
169 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
20263082
LP
170
171 if (u->display)
9444b1f2
LP
172 fprintf(f, "DISPLAY=%s\n", u->display->id);
173
174 if (dual_timestamp_is_set(&u->timestamp))
20263082 175 fprintf(f,
90b2de37
ZJS
176 "REALTIME="USEC_FMT"\n"
177 "MONOTONIC="USEC_FMT"\n",
178 u->timestamp.realtime,
179 u->timestamp.monotonic);
20263082 180
034a2a52
LP
181 if (u->sessions) {
182 Session *i;
9b958eff 183 bool first;
034a2a52
LP
184
185 fputs("SESSIONS=", f);
9b958eff 186 first = true;
034a2a52 187 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
188 if (first)
189 first = false;
190 else
191 fputc(' ', f);
192
193 fputs(i->id, f);
034a2a52
LP
194 }
195
9b958eff
LP
196 fputs("\nSEATS=", f);
197 first = true;
034a2a52 198 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
199 if (!i->seat)
200 continue;
201
202 if (first)
203 first = false;
204 else
205 fputc(' ', f);
206
207 fputs(i->seat->id, f);
034a2a52
LP
208 }
209
9b958eff
LP
210 fputs("\nACTIVE_SESSIONS=", f);
211 first = true;
212 LIST_FOREACH(sessions_by_user, i, u->sessions) {
213 if (!session_is_active(i))
214 continue;
215
216 if (first)
217 first = false;
218 else
219 fputc(' ', f);
220
221 fputs(i->id, f);
222 }
034a2a52 223
2dc8f41a
CG
224 fputs("\nONLINE_SESSIONS=", f);
225 first = true;
226 LIST_FOREACH(sessions_by_user, i, u->sessions) {
227 if (session_get_state(i) == SESSION_CLOSING)
228 continue;
229
230 if (first)
231 first = false;
232 else
233 fputc(' ', f);
234
235 fputs(i->id, f);
236 }
237
9b958eff
LP
238 fputs("\nACTIVE_SEATS=", f);
239 first = true;
034a2a52 240 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
241 if (!session_is_active(i) || !i->seat)
242 continue;
243
244 if (first)
245 first = false;
246 else
47acb2f1
CG
247 fputc(' ', f);
248
249 fputs(i->seat->id, f);
034a2a52 250 }
2dc8f41a
CG
251
252 fputs("\nONLINE_SEATS=", f);
253 first = true;
254 LIST_FOREACH(sessions_by_user, i, u->sessions) {
255 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
256 continue;
257
258 if (first)
259 first = false;
260 else
261 fputc(' ', f);
262
263 fputs(i->seat->id, f);
264 }
9b958eff 265 fputc('\n', f);
034a2a52
LP
266 }
267
dacd6cee
LP
268 r = fflush_and_check(f);
269 if (r < 0)
270 goto fail;
14c3baca 271
dacd6cee 272 if (rename(temp_path, u->state_file) < 0) {
20263082 273 r = -errno;
dacd6cee 274 goto fail;
20263082
LP
275 }
276
dacd6cee
LP
277 return 0;
278
279fail:
280 (void) unlink(u->state_file);
281
282 if (temp_path)
283 (void) unlink(temp_path);
14c3baca 284
dacd6cee 285 return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
20263082
LP
286}
287
71161305
SM
288int user_save(User *u) {
289 assert(u);
290
291 if (!u->started)
292 return 0;
293
294 return user_save_internal (u);
295}
296
20263082 297int user_load(User *u) {
9444b1f2 298 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
98a28fef 299 Session *s = NULL;
9444b1f2 300 int r;
20263082
LP
301
302 assert(u);
303
a185c5aa 304 r = parse_env_file(u->state_file, NEWLINE,
fb6becb4
LP
305 "SERVICE", &u->service,
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) {
6230bf75
DH
396 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
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) {
cc377381 427 _cleanup_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
fb6becb4 433 if (!u->service) {
de0671ee 434 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
fb6becb4 435
90558f31 436 xsprintf(lu, UID_FMT, u->uid);
7410616c
LP
437 r = unit_name_build("user", lu, ".service", &service);
438 if (r < 0)
439 return log_error_errno(r, "Failed to build service name: %m");
fb6becb4 440
90558f31
LP
441 r = manager_start_unit(
442 u->manager,
443 service,
444 &error,
445 &job);
d0af76e6 446 if (r < 0) {
90558f31 447 log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
d0af76e6 448 free(service);
90558f31 449 /* we don't fail due to this, let's try to continue */
d0af76e6
LP
450 } else {
451 u->service = service;
452
453 free(u->service_job);
454 u->service_job = job;
455 }
fb6becb4
LP
456 }
457
d0af76e6 458 if (u->service)
90558f31 459 (void) hashmap_put(u->manager->user_units, u->service, u);
d0af76e6 460
20263082
LP
461 return 0;
462}
463
464int user_start(User *u) {
465 int r;
466
467 assert(u);
468
9418f147
LP
469 if (u->started)
470 return 0;
471
1637a8be 472 log_debug("New user %s logged in.", u->name);
ed18b08b 473
20263082
LP
474 /* Make XDG_RUNTIME_DIR */
475 r = user_mkdir_runtime_path(u);
476 if (r < 0)
477 return r;
478
479 /* Create cgroup */
fb6becb4 480 r = user_start_slice(u);
20263082
LP
481 if (r < 0)
482 return r;
483
71161305
SM
484 /* Save the user data so far, because pam_systemd will read the
485 * XDG_RUNTIME_DIR out of it while starting up systemd --user.
486 * We need to do user_save_internal() because we have not
487 * "officially" started yet. */
488 user_save_internal(u);
489
20263082
LP
490 /* Spawn user systemd */
491 r = user_start_service(u);
492 if (r < 0)
493 return r;
494
9444b1f2
LP
495 if (!dual_timestamp_is_set(&u->timestamp))
496 dual_timestamp_get(&u->timestamp);
20263082 497
9418f147
LP
498 u->started = true;
499
7f7bb946
LP
500 /* Save new user data */
501 user_save(u);
502
da119395
LP
503 user_send_signal(u, true);
504
20263082
LP
505 return 0;
506}
507
fb6becb4 508static int user_stop_slice(User *u) {
cc377381 509 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
510 char *job;
511 int r;
20263082 512
20263082
LP
513 assert(u);
514
fb6becb4
LP
515 r = manager_stop_unit(u->manager, u->slice, &error, &job);
516 if (r < 0) {
cc377381 517 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
518 return r;
519 }
ed18b08b 520
fb6becb4
LP
521 free(u->slice_job);
522 u->slice_job = job;
ed18b08b 523
fb6becb4 524 return r;
20263082
LP
525}
526
fb6becb4 527static int user_stop_service(User *u) {
cc377381 528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 529 char *job;
20263082 530 int r;
20263082
LP
531
532 assert(u);
533
fb6becb4 534 if (!u->service)
20263082
LP
535 return 0;
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}