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