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