]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
logind: fix serialization of ListSeats() bus call
[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
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25
20263082 26#include "util.h"
49e942b2 27#include "mkdir.h"
20263082
LP
28#include "hashmap.h"
29#include "strv.h"
a5c32cff 30#include "fileio.h"
9444b1f2 31#include "special.h"
fb6becb4 32#include "unit-name.h"
cc377381
LP
33#include "bus-util.h"
34#include "bus-error.h"
fb6becb4 35#include "logind-user.h"
20263082
LP
36
37User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
38 User *u;
39
40 assert(m);
41 assert(name);
42
14c3baca 43 u = new0(User, 1);
20263082
LP
44 if (!u)
45 return NULL;
46
47 u->name = strdup(name);
9444b1f2
LP
48 if (!u->name)
49 goto fail;
20263082 50
9444b1f2
LP
51 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
52 goto fail;
20263082 53
9444b1f2
LP
54 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
55 goto fail;
20263082
LP
56
57 u->manager = m;
58 u->uid = uid;
59 u->gid = gid;
60
61 return u;
9444b1f2
LP
62
63fail:
64 free(u->state_file);
65 free(u->name);
66 free(u);
67
68 return NULL;
20263082
LP
69}
70
71void user_free(User *u) {
72 assert(u);
73
14c3baca 74 if (u->in_gc_queue)
71fda00f 75 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
14c3baca 76
20263082
LP
77 while (u->sessions)
78 session_free(u->sessions);
79
fb6becb4
LP
80 if (u->slice) {
81 hashmap_remove(u->manager->user_units, u->slice);
82 free(u->slice);
9444b1f2 83 }
20263082 84
fb6becb4
LP
85 if (u->service) {
86 hashmap_remove(u->manager->user_units, u->service);
87 free(u->service);
88 }
89
90 free(u->slice_job);
91 free(u->service_job);
92
20263082
LP
93 free(u->runtime_path);
94
95 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
96
97 free(u->name);
d2f92cdf 98 free(u->state_file);
20263082
LP
99 free(u);
100}
101
102int user_save(User *u) {
9444b1f2
LP
103 _cleanup_free_ char *temp_path = NULL;
104 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
105 int r;
106
107 assert(u);
108 assert(u->state_file);
109
accaeded
LP
110 if (!u->started)
111 return 0;
112
d2e54fae 113 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 114 if (r < 0)
14c3baca 115 goto finish;
20263082 116
14c3baca
LP
117 r = fopen_temporary(u->state_file, &f, &temp_path);
118 if (r < 0)
119 goto finish;
120
121 fchmod(fileno(f), 0644);
20263082
LP
122
123 fprintf(f,
14c3baca 124 "# This is private data. Do not parse.\n"
20263082
LP
125 "NAME=%s\n"
126 "STATE=%s\n",
127 u->name,
128 user_state_to_string(user_get_state(u)));
129
20263082 130 if (u->runtime_path)
9444b1f2 131 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
20263082
LP
132
133 if (u->service)
9444b1f2 134 fprintf(f, "SERVICE=%s\n", u->service);
fb6becb4
LP
135 if (u->service_job)
136 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
9444b1f2
LP
137
138 if (u->slice)
139 fprintf(f, "SLICE=%s\n", u->slice);
fb6becb4
LP
140 if (u->slice_job)
141 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
20263082
LP
142
143 if (u->display)
9444b1f2
LP
144 fprintf(f, "DISPLAY=%s\n", u->display->id);
145
146 if (dual_timestamp_is_set(&u->timestamp))
20263082 147 fprintf(f,
9444b1f2
LP
148 "REALTIME=%llu\n"
149 "MONOTONIC=%llu\n",
150 (unsigned long long) u->timestamp.realtime,
151 (unsigned long long) u->timestamp.monotonic);
20263082 152
034a2a52
LP
153 if (u->sessions) {
154 Session *i;
9b958eff 155 bool first;
034a2a52
LP
156
157 fputs("SESSIONS=", f);
9b958eff 158 first = true;
034a2a52 159 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
160 if (first)
161 first = false;
162 else
163 fputc(' ', f);
164
165 fputs(i->id, f);
034a2a52
LP
166 }
167
9b958eff
LP
168 fputs("\nSEATS=", f);
169 first = true;
034a2a52 170 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
171 if (!i->seat)
172 continue;
173
174 if (first)
175 first = false;
176 else
177 fputc(' ', f);
178
179 fputs(i->seat->id, f);
034a2a52
LP
180 }
181
9b958eff
LP
182 fputs("\nACTIVE_SESSIONS=", f);
183 first = true;
184 LIST_FOREACH(sessions_by_user, i, u->sessions) {
185 if (!session_is_active(i))
186 continue;
187
188 if (first)
189 first = false;
190 else
191 fputc(' ', f);
192
193 fputs(i->id, f);
194 }
034a2a52 195
2dc8f41a
CG
196 fputs("\nONLINE_SESSIONS=", f);
197 first = true;
198 LIST_FOREACH(sessions_by_user, i, u->sessions) {
199 if (session_get_state(i) == SESSION_CLOSING)
200 continue;
201
202 if (first)
203 first = false;
204 else
205 fputc(' ', f);
206
207 fputs(i->id, f);
208 }
209
9b958eff
LP
210 fputs("\nACTIVE_SEATS=", f);
211 first = true;
034a2a52 212 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
213 if (!session_is_active(i) || !i->seat)
214 continue;
215
216 if (first)
217 first = false;
218 else
47acb2f1
CG
219 fputc(' ', f);
220
221 fputs(i->seat->id, f);
034a2a52 222 }
2dc8f41a
CG
223
224 fputs("\nONLINE_SEATS=", f);
225 first = true;
226 LIST_FOREACH(sessions_by_user, i, u->sessions) {
227 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
228 continue;
229
230 if (first)
231 first = false;
232 else
233 fputc(' ', f);
234
235 fputs(i->seat->id, f);
236 }
9b958eff 237 fputc('\n', f);
034a2a52
LP
238 }
239
20263082 240 fflush(f);
14c3baca
LP
241
242 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
20263082
LP
243 r = -errno;
244 unlink(u->state_file);
14c3baca 245 unlink(temp_path);
20263082
LP
246 }
247
14c3baca
LP
248finish:
249 if (r < 0)
250 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
251
20263082
LP
252 return r;
253}
254
255int user_load(User *u) {
9444b1f2 256 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
98a28fef 257 Session *s = NULL;
9444b1f2 258 int r;
20263082
LP
259
260 assert(u);
261
a185c5aa 262 r = parse_env_file(u->state_file, NEWLINE,
fb6becb4
LP
263 "RUNTIME", &u->runtime_path,
264 "SERVICE", &u->service,
265 "SERVICE_JOB", &u->service_job,
266 "SLICE", &u->slice,
267 "SLICE_JOB", &u->slice_job,
268 "DISPLAY", &display,
269 "REALTIME", &realtime,
270 "MONOTONIC", &monotonic,
20263082
LP
271 NULL);
272 if (r < 0) {
20263082
LP
273 if (r == -ENOENT)
274 return 0;
275
276 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
277 return r;
278 }
279
9444b1f2 280 if (display)
98a28fef 281 s = hashmap_get(u->manager->sessions, display);
20263082 282
4d6d6518 283 if (s && s->display && display_is_local(s->display))
20263082
LP
284 u->display = s;
285
9444b1f2
LP
286 if (realtime) {
287 unsigned long long l;
288 if (sscanf(realtime, "%llu", &l) > 0)
289 u->timestamp.realtime = l;
290 }
291
292 if (monotonic) {
293 unsigned long long l;
294 if (sscanf(monotonic, "%llu", &l) > 0)
295 u->timestamp.monotonic = l;
296 }
297
20263082
LP
298 return r;
299}
300
301static int user_mkdir_runtime_path(User *u) {
302 char *p;
303 int r;
304
305 assert(u);
306
d2e54fae 307 r = mkdir_safe_label("/run/user", 0755, 0, 0);
20263082
LP
308 if (r < 0) {
309 log_error("Failed to create /run/user: %s", strerror(-r));
310 return r;
311 }
312
313 if (!u->runtime_path) {
0d0f0c50
SL
314 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
315 return log_oom();
20263082
LP
316 } else
317 p = u->runtime_path;
318
d2e54fae 319 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
20263082
LP
320 if (r < 0) {
321 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
322 free(p);
323 u->runtime_path = NULL;
324 return r;
325 }
326
327 u->runtime_path = p;
328 return 0;
329}
330
fb6becb4 331static int user_start_slice(User *u) {
fb6becb4 332 char *job;
20263082
LP
333 int r;
334
335 assert(u);
336
fb6becb4 337 if (!u->slice) {
cc377381 338 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d0af76e6 339 char lu[DECIMAL_STR_MAX(unsigned long) + 1], *slice;
fb6becb4 340 sprintf(lu, "%lu", (unsigned long) u->uid);
ae018d9b 341
d0af76e6 342 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
9444b1f2
LP
343 if (r < 0)
344 return r;
345
d0af76e6
LP
346 r = manager_start_unit(u->manager, slice, &error, &job);
347 if (r < 0) {
cc377381 348 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
d0af76e6
LP
349 free(slice);
350 } else {
351 u->slice = slice;
352
353 free(u->slice_job);
354 u->slice_job = job;
355 }
20263082
LP
356 }
357
d0af76e6
LP
358 if (u->slice)
359 hashmap_put(u->manager->user_units, u->slice, u);
360
20263082
LP
361 return 0;
362}
363
364static int user_start_service(User *u) {
cc377381 365 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
366 char *job;
367 int r;
368
20263082
LP
369 assert(u);
370
fb6becb4 371 if (!u->service) {
d0af76e6 372 char lu[DECIMAL_STR_MAX(unsigned long) + 1], *service;
fb6becb4
LP
373 sprintf(lu, "%lu", (unsigned long) u->uid);
374
d0af76e6
LP
375 service = unit_name_build("user", lu, ".service");
376 if (!service)
fb6becb4
LP
377 return log_oom();
378
d0af76e6
LP
379 r = manager_start_unit(u->manager, service, &error, &job);
380 if (r < 0) {
cc377381 381 log_error("Failed to start user service: %s", bus_error_message(&error, r));
d0af76e6
LP
382 free(service);
383 } else {
384 u->service = service;
385
386 free(u->service_job);
387 u->service_job = job;
388 }
fb6becb4
LP
389 }
390
d0af76e6
LP
391 if (u->service)
392 hashmap_put(u->manager->user_units, u->service, u);
393
20263082
LP
394 return 0;
395}
396
397int user_start(User *u) {
398 int r;
399
400 assert(u);
401
9418f147
LP
402 if (u->started)
403 return 0;
404
1637a8be 405 log_debug("New user %s logged in.", u->name);
ed18b08b 406
20263082
LP
407 /* Make XDG_RUNTIME_DIR */
408 r = user_mkdir_runtime_path(u);
409 if (r < 0)
410 return r;
411
412 /* Create cgroup */
fb6becb4 413 r = user_start_slice(u);
20263082
LP
414 if (r < 0)
415 return r;
416
417 /* Spawn user systemd */
418 r = user_start_service(u);
419 if (r < 0)
420 return r;
421
9444b1f2
LP
422 if (!dual_timestamp_is_set(&u->timestamp))
423 dual_timestamp_get(&u->timestamp);
20263082 424
9418f147
LP
425 u->started = true;
426
7f7bb946
LP
427 /* Save new user data */
428 user_save(u);
429
da119395
LP
430 user_send_signal(u, true);
431
20263082
LP
432 return 0;
433}
434
fb6becb4 435static int user_stop_slice(User *u) {
cc377381 436 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
437 char *job;
438 int r;
20263082 439
20263082
LP
440 assert(u);
441
fb6becb4
LP
442 if (!u->slice)
443 return 0;
444
445 r = manager_stop_unit(u->manager, u->slice, &error, &job);
446 if (r < 0) {
cc377381 447 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
448 return r;
449 }
ed18b08b 450
fb6becb4
LP
451 free(u->slice_job);
452 u->slice_job = job;
ed18b08b 453
fb6becb4 454 return r;
20263082
LP
455}
456
fb6becb4 457static int user_stop_service(User *u) {
cc377381 458 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 459 char *job;
20263082 460 int r;
20263082
LP
461
462 assert(u);
463
fb6becb4 464 if (!u->service)
20263082
LP
465 return 0;
466
fb6becb4
LP
467 r = manager_stop_unit(u->manager, u->service, &error, &job);
468 if (r < 0) {
cc377381 469 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
fb6becb4
LP
470 return r;
471 }
20263082 472
fb6becb4
LP
473 free(u->service_job);
474 u->service_job = job;
20263082 475
fb6becb4
LP
476 return r;
477}
20263082 478
20263082
LP
479static int user_remove_runtime_path(User *u) {
480 int r;
481
482 assert(u);
483
484 if (!u->runtime_path)
485 return 0;
486
ad293f5a 487 r = rm_rf(u->runtime_path, false, true, false);
20263082
LP
488 if (r < 0)
489 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
490
491 free(u->runtime_path);
492 u->runtime_path = NULL;
493
494 return r;
495}
496
497int user_stop(User *u) {
498 Session *s;
499 int r = 0, k;
500 assert(u);
501
502 LIST_FOREACH(sessions_by_user, s, u->sessions) {
503 k = session_stop(s);
504 if (k < 0)
505 r = k;
506 }
507
508 /* Kill systemd */
509 k = user_stop_service(u);
510 if (k < 0)
511 r = k;
512
513 /* Kill cgroup */
fb6becb4 514 k = user_stop_slice(u);
20263082
LP
515 if (k < 0)
516 r = k;
517
405e0255
LP
518 user_save(u);
519
520 return r;
521}
522
523int user_finalize(User *u) {
524 Session *s;
525 int r = 0, k;
526
527 assert(u);
528
529 if (u->started)
530 log_debug("User %s logged out.", u->name);
531
532 LIST_FOREACH(sessions_by_user, s, u->sessions) {
533 k = session_finalize(s);
534 if (k < 0)
535 r = k;
536 }
537
20263082
LP
538 /* Kill XDG_RUNTIME_DIR */
539 k = user_remove_runtime_path(u);
540 if (k < 0)
541 r = k;
542
d2f92cdf
LP
543 unlink(u->state_file);
544 user_add_to_gc_queue(u);
545
405e0255 546 if (u->started) {
ed18b08b 547 user_send_signal(u, false);
405e0255
LP
548 u->started = false;
549 }
9418f147 550
20263082
LP
551 return r;
552}
553
a185c5aa
LP
554int user_get_idle_hint(User *u, dual_timestamp *t) {
555 Session *s;
556 bool idle_hint = true;
557 dual_timestamp ts = { 0, 0 };
558
559 assert(u);
560
561 LIST_FOREACH(sessions_by_user, s, u->sessions) {
562 dual_timestamp k;
563 int ih;
564
565 ih = session_get_idle_hint(s, &k);
566 if (ih < 0)
567 return ih;
568
569 if (!ih) {
570 if (!idle_hint) {
571 if (k.monotonic < ts.monotonic)
572 ts = k;
573 } else {
574 idle_hint = false;
575 ts = k;
576 }
577 } else if (idle_hint) {
578
579 if (k.monotonic > ts.monotonic)
580 ts = k;
581 }
582 }
583
584 if (t)
585 *t = ts;
586
587 return idle_hint;
588}
589
e96cd586 590static int user_check_linger_file(User *u) {
cc377381
LP
591 _cleanup_free_ char *cc = NULL;
592 char *p = NULL;
e96cd586 593
cc377381
LP
594 cc = cescape(u->name);
595 if (!cc)
e96cd586
LP
596 return -ENOMEM;
597
cc377381 598 p = strappenda("/var/lib/systemd/linger/", cc);
e96cd586 599
cc377381 600 return access(p, F_OK) >= 0;
e96cd586
LP
601}
602
cc377381 603bool user_check_gc(User *u, bool drop_not_started) {
20263082
LP
604 assert(u);
605
4a4b033f 606 if (drop_not_started && !u->started)
cc377381 607 return false;
932e3ee7 608
20263082 609 if (u->sessions)
cc377381 610 return true;
20263082 611
e96cd586 612 if (user_check_linger_file(u) > 0)
cc377381 613 return true;
20263082 614
cc377381
LP
615 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
616 return true;
617
618 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
619 return true;
405e0255
LP
620
621 if (u->slice && manager_unit_is_active(u->manager, u->slice) != 0)
cc377381 622 return true;
405e0255
LP
623
624 if (u->service && manager_unit_is_active(u->manager, u->service) != 0)
cc377381 625 return true;
405e0255 626
cc377381 627 return false;
20263082
LP
628}
629
14c3baca
LP
630void user_add_to_gc_queue(User *u) {
631 assert(u);
632
633 if (u->in_gc_queue)
634 return;
635
71fda00f 636 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
14c3baca
LP
637 u->in_gc_queue = true;
638}
639
20263082
LP
640UserState user_get_state(User *u) {
641 Session *i;
c9caad80 642 bool all_closing = true;
20263082
LP
643
644 assert(u);
645
405e0255
LP
646 if (u->closing)
647 return USER_CLOSING;
648
fb6becb4 649 if (u->slice_job || u->service_job)
405e0255 650 return USER_OPENING;
c9caad80
CG
651
652 LIST_FOREACH(sessions_by_user, i, u->sessions) {
20263082
LP
653 if (session_is_active(i))
654 return USER_ACTIVE;
c9caad80
CG
655 if (session_get_state(i) != SESSION_CLOSING)
656 all_closing = false;
657 }
20263082 658
e96cd586 659 if (u->sessions)
c9caad80 660 return all_closing ? USER_CLOSING : USER_ONLINE;
e96cd586
LP
661
662 if (user_check_linger_file(u) > 0)
663 return USER_LINGERING;
664
665 return USER_CLOSING;
20263082
LP
666}
667
de07ab16 668int user_kill(User *u, int signo) {
de07ab16
LP
669 assert(u);
670
fb6becb4 671 if (!u->slice)
de07ab16
LP
672 return -ESRCH;
673
fb6becb4 674 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
de07ab16
LP
675}
676
20263082
LP
677static const char* const user_state_table[_USER_STATE_MAX] = {
678 [USER_OFFLINE] = "offline",
fb6becb4 679 [USER_OPENING] = "opening",
20263082
LP
680 [USER_LINGERING] = "lingering",
681 [USER_ONLINE] = "online",
e96cd586
LP
682 [USER_ACTIVE] = "active",
683 [USER_CLOSING] = "closing"
20263082
LP
684};
685
686DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);