]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
systemctl: show main and control PID explicitly in cgroup-show
[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
90821c93 26#include "logind-user.h"
20263082 27#include "util.h"
49e942b2 28#include "mkdir.h"
20263082
LP
29#include "cgroup-util.h"
30#include "hashmap.h"
31#include "strv.h"
32
33User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
34 User *u;
35
36 assert(m);
37 assert(name);
38
14c3baca 39 u = new0(User, 1);
20263082
LP
40 if (!u)
41 return NULL;
42
43 u->name = strdup(name);
44 if (!u->name) {
45 free(u);
46 return NULL;
47 }
48
98a28fef 49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
20263082
LP
50 free(u->name);
51 free(u);
52 return NULL;
53 }
54
55 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
56 free(u->state_file);
57 free(u->name);
58 free(u);
59 return NULL;
60 }
61
62 u->manager = m;
63 u->uid = uid;
64 u->gid = gid;
65
66 return u;
67}
68
69void user_free(User *u) {
70 assert(u);
71
14c3baca
LP
72 if (u->in_gc_queue)
73 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
74
20263082
LP
75 while (u->sessions)
76 session_free(u->sessions);
77
78 free(u->cgroup_path);
79
80 free(u->service);
81 free(u->runtime_path);
82
83 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
84
85 free(u->name);
d2f92cdf 86 free(u->state_file);
20263082
LP
87 free(u);
88}
89
90int user_save(User *u) {
91 FILE *f;
92 int r;
14c3baca 93 char *temp_path;
20263082
LP
94
95 assert(u);
96 assert(u->state_file);
97
accaeded
LP
98 if (!u->started)
99 return 0;
100
98a28fef 101 r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
20263082 102 if (r < 0)
14c3baca 103 goto finish;
20263082 104
14c3baca
LP
105 r = fopen_temporary(u->state_file, &f, &temp_path);
106 if (r < 0)
107 goto finish;
108
109 fchmod(fileno(f), 0644);
20263082
LP
110
111 fprintf(f,
14c3baca 112 "# This is private data. Do not parse.\n"
20263082
LP
113 "NAME=%s\n"
114 "STATE=%s\n",
115 u->name,
116 user_state_to_string(user_get_state(u)));
117
118 if (u->cgroup_path)
119 fprintf(f,
120 "CGROUP=%s\n",
121 u->cgroup_path);
122
123 if (u->runtime_path)
124 fprintf(f,
125 "RUNTIME=%s\n",
126 u->runtime_path);
127
128 if (u->service)
129 fprintf(f,
130 "SERVICE=%s\n",
131 u->service);
132
133 if (u->display)
134 fprintf(f,
135 "DISPLAY=%s\n",
136 u->display->id);
137
034a2a52
LP
138 if (u->sessions) {
139 Session *i;
140
141 fputs("SESSIONS=", f);
142 LIST_FOREACH(sessions_by_user, i, u->sessions) {
143 fprintf(f,
144 "%s%c",
145 i->id,
6c7506dc 146 i->sessions_by_user_next ? ' ' : '\n');
034a2a52
LP
147 }
148
149 fputs("SEATS=", f);
150 LIST_FOREACH(sessions_by_user, i, u->sessions) {
151 if (i->seat)
152 fprintf(f,
153 "%s%c",
154 i->seat->id,
6c7506dc 155 i->sessions_by_user_next ? ' ' : '\n');
034a2a52
LP
156 }
157
158 fputs("ACTIVE_SESSIONS=", f);
159 LIST_FOREACH(sessions_by_user, i, u->sessions)
160 if (session_is_active(i))
161 fprintf(f,
162 "%lu%c",
163 (unsigned long) i->user->uid,
6c7506dc 164 i->sessions_by_user_next ? ' ' : '\n');
034a2a52
LP
165
166 fputs("ACTIVE_SEATS=", f);
167 LIST_FOREACH(sessions_by_user, i, u->sessions) {
168 if (session_is_active(i) && i->seat)
169 fprintf(f,
170 "%s%c",
171 i->seat->id,
6c7506dc 172 i->sessions_by_user_next ? ' ' : '\n');
034a2a52
LP
173 }
174 }
175
20263082 176 fflush(f);
14c3baca
LP
177
178 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
20263082
LP
179 r = -errno;
180 unlink(u->state_file);
14c3baca 181 unlink(temp_path);
20263082
LP
182 }
183
184 fclose(f);
14c3baca
LP
185 free(temp_path);
186
187finish:
188 if (r < 0)
189 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
190
20263082
LP
191 return r;
192}
193
194int user_load(User *u) {
195 int r;
196 char *display = NULL;
98a28fef 197 Session *s = NULL;
20263082
LP
198
199 assert(u);
200
a185c5aa 201 r = parse_env_file(u->state_file, NEWLINE,
20263082
LP
202 "CGROUP", &u->cgroup_path,
203 "RUNTIME", &u->runtime_path,
204 "SERVICE", &u->service,
205 "DISPLAY", &display,
206 NULL);
207 if (r < 0) {
208 free(display);
209
210 if (r == -ENOENT)
211 return 0;
212
213 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
214 return r;
215 }
216
98a28fef
LP
217 if (display) {
218 s = hashmap_get(u->manager->sessions, display);
219 free(display);
220 }
20263082 221
4d6d6518 222 if (s && s->display && display_is_local(s->display))
20263082
LP
223 u->display = s;
224
225 return r;
226}
227
228static int user_mkdir_runtime_path(User *u) {
229 char *p;
230 int r;
231
232 assert(u);
233
234 r = safe_mkdir("/run/user", 0755, 0, 0);
235 if (r < 0) {
236 log_error("Failed to create /run/user: %s", strerror(-r));
237 return r;
238 }
239
240 if (!u->runtime_path) {
241 p = strappend("/run/user/", u->name);
242
243 if (!p) {
244 log_error("Out of memory");
245 return -ENOMEM;
246 }
247 } else
248 p = u->runtime_path;
249
250 r = safe_mkdir(p, 0700, u->uid, u->gid);
251 if (r < 0) {
252 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
253 free(p);
254 u->runtime_path = NULL;
255 return r;
256 }
257
258 u->runtime_path = p;
259 return 0;
260}
261
262static int user_create_cgroup(User *u) {
263 char **k;
264 char *p;
265 int r;
266
267 assert(u);
268
269 if (!u->cgroup_path) {
270 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
271 log_error("Out of memory");
272 return -ENOMEM;
273 }
274 } else
275 p = u->cgroup_path;
276
277 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
278 if (r < 0) {
4d6d6518 279 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
20263082
LP
280 free(p);
281 u->cgroup_path = NULL;
20263082
LP
282 return r;
283 }
284
285 u->cgroup_path = p;
286
287 STRV_FOREACH(k, u->manager->controllers) {
193197e8
LP
288
289 if (strv_contains(u->manager->reset_controllers, *k))
290 continue;
291
20263082
LP
292 r = cg_create(*k, p);
293 if (r < 0)
294 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
295 }
296
297 return 0;
298}
299
300static int user_start_service(User *u) {
301 assert(u);
302
129eebe0
LP
303 /* FIXME: Fill me in later ... */
304
20263082
LP
305 return 0;
306}
307
308int user_start(User *u) {
309 int r;
310
311 assert(u);
312
9418f147
LP
313 if (u->started)
314 return 0;
315
1637a8be 316 log_debug("New user %s logged in.", u->name);
ed18b08b 317
20263082
LP
318 /* Make XDG_RUNTIME_DIR */
319 r = user_mkdir_runtime_path(u);
320 if (r < 0)
321 return r;
322
323 /* Create cgroup */
324 r = user_create_cgroup(u);
325 if (r < 0)
326 return r;
327
328 /* Spawn user systemd */
329 r = user_start_service(u);
330 if (r < 0)
331 return r;
332
333 dual_timestamp_get(&u->timestamp);
334
9418f147
LP
335 u->started = true;
336
7f7bb946
LP
337 /* Save new user data */
338 user_save(u);
339
da119395
LP
340 user_send_signal(u, true);
341
20263082
LP
342 return 0;
343}
344
345static int user_stop_service(User *u) {
346 assert(u);
347
348 if (!u->service)
349 return 0;
350
351 return 0;
352}
353
354static int user_shall_kill(User *u) {
355 assert(u);
356
ed18b08b
LP
357 if (!u->manager->kill_user_processes)
358 return false;
359
360 if (strv_contains(u->manager->kill_exclude_users, u->name))
361 return false;
362
363 if (strv_isempty(u->manager->kill_only_users))
364 return true;
365
366 return strv_contains(u->manager->kill_only_users, u->name);
20263082
LP
367}
368
de07ab16 369static int user_terminate_cgroup(User *u) {
20263082
LP
370 int r;
371 char **k;
372
373 assert(u);
374
375 if (!u->cgroup_path)
376 return 0;
377
378 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
379
380 if (user_shall_kill(u)) {
381
382 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
383 if (r < 0)
384 log_error("Failed to kill user cgroup: %s", strerror(-r));
385 } else {
386
387 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
388 if (r < 0)
389 log_error("Failed to check user cgroup: %s", strerror(-r));
390 else if (r > 0) {
391 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
392 if (r < 0)
393 log_error("Failed to delete user cgroup: %s", strerror(-r));
394 } else
395 r = -EBUSY;
396 }
397
398 STRV_FOREACH(k, u->manager->controllers)
399 cg_trim(*k, u->cgroup_path, true);
400
401 free(u->cgroup_path);
402 u->cgroup_path = NULL;
403
404 return r;
405}
406
407static int user_remove_runtime_path(User *u) {
408 int r;
409
410 assert(u);
411
412 if (!u->runtime_path)
413 return 0;
414
ad293f5a 415 r = rm_rf(u->runtime_path, false, true, false);
20263082
LP
416 if (r < 0)
417 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
418
419 free(u->runtime_path);
420 u->runtime_path = NULL;
421
422 return r;
423}
424
425int user_stop(User *u) {
426 Session *s;
427 int r = 0, k;
428 assert(u);
429
ed18b08b 430 if (u->started)
1637a8be 431 log_debug("User %s logged out.", u->name);
9418f147 432
20263082
LP
433 LIST_FOREACH(sessions_by_user, s, u->sessions) {
434 k = session_stop(s);
435 if (k < 0)
436 r = k;
437 }
438
439 /* Kill systemd */
440 k = user_stop_service(u);
441 if (k < 0)
442 r = k;
443
444 /* Kill cgroup */
de07ab16 445 k = user_terminate_cgroup(u);
20263082
LP
446 if (k < 0)
447 r = k;
448
449 /* Kill XDG_RUNTIME_DIR */
450 k = user_remove_runtime_path(u);
451 if (k < 0)
452 r = k;
453
d2f92cdf
LP
454 unlink(u->state_file);
455 user_add_to_gc_queue(u);
456
ed18b08b
LP
457 if (u->started)
458 user_send_signal(u, false);
459
9418f147
LP
460 u->started = false;
461
20263082
LP
462 return r;
463}
464
a185c5aa
LP
465int user_get_idle_hint(User *u, dual_timestamp *t) {
466 Session *s;
467 bool idle_hint = true;
468 dual_timestamp ts = { 0, 0 };
469
470 assert(u);
471
472 LIST_FOREACH(sessions_by_user, s, u->sessions) {
473 dual_timestamp k;
474 int ih;
475
476 ih = session_get_idle_hint(s, &k);
477 if (ih < 0)
478 return ih;
479
480 if (!ih) {
481 if (!idle_hint) {
482 if (k.monotonic < ts.monotonic)
483 ts = k;
484 } else {
485 idle_hint = false;
486 ts = k;
487 }
488 } else if (idle_hint) {
489
490 if (k.monotonic > ts.monotonic)
491 ts = k;
492 }
493 }
494
495 if (t)
496 *t = ts;
497
498 return idle_hint;
499}
500
4a4b033f 501int user_check_gc(User *u, bool drop_not_started) {
20263082
LP
502 int r;
503 char *p;
504
505 assert(u);
506
4a4b033f 507 if (drop_not_started && !u->started)
932e3ee7
LP
508 return 0;
509
20263082
LP
510 if (u->sessions)
511 return 1;
512
513 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
514 return -ENOMEM;
515
516 r = access(p, F_OK) >= 0;
517 free(p);
518
519 if (r > 0)
520 return 1;
521
522 if (u->cgroup_path) {
523 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
524 if (r < 0)
525 return r;
526
527 if (r <= 0)
528 return 1;
529 }
530
531 return 0;
532}
533
14c3baca
LP
534void user_add_to_gc_queue(User *u) {
535 assert(u);
536
537 if (u->in_gc_queue)
538 return;
539
540 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
541 u->in_gc_queue = true;
542}
543
20263082
LP
544UserState user_get_state(User *u) {
545 Session *i;
546
547 assert(u);
548
549 if (!u->sessions)
550 return USER_LINGERING;
551
552 LIST_FOREACH(sessions_by_user, i, u->sessions)
553 if (session_is_active(i))
554 return USER_ACTIVE;
555
556 return USER_ONLINE;
557}
558
de07ab16
LP
559int user_kill(User *u, int signo) {
560 int r = 0, q;
561 Set *pid_set = NULL;
562
563 assert(u);
564
565 if (!u->cgroup_path)
566 return -ESRCH;
567
568 pid_set = set_new(trivial_hash_func, trivial_compare_func);
569 if (!pid_set)
570 return -ENOMEM;
571
572 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
573 if (q < 0)
574 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
575 r = q;
576
de07ab16
LP
577 if (pid_set)
578 set_free(pid_set);
579
580 return r;
581}
582
20263082
LP
583static const char* const user_state_table[_USER_STATE_MAX] = {
584 [USER_OFFLINE] = "offline",
585 [USER_LINGERING] = "lingering",
586 [USER_ONLINE] = "online",
587 [USER_ACTIVE] = "active"
588};
589
590DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);