]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / login / logind-user.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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
26 #include "logind-user.h"
27 #include "util.h"
28 #include "mkdir.h"
29 #include "cgroup-util.h"
30 #include "hashmap.h"
31 #include "strv.h"
32
33 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
34 User *u;
35
36 assert(m);
37 assert(name);
38
39 u = new0(User, 1);
40 if (!u)
41 return NULL;
42
43 u->name = strdup(name);
44 if (!u->name) {
45 free(u);
46 return NULL;
47 }
48
49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
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
69 void user_free(User *u) {
70 assert(u);
71
72 if (u->in_gc_queue)
73 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
74
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);
86 free(u->state_file);
87 free(u);
88 }
89
90 int user_save(User *u) {
91 FILE *f;
92 int r;
93 char *temp_path;
94
95 assert(u);
96 assert(u->state_file);
97
98 if (!u->started)
99 return 0;
100
101 r = safe_mkdir("/run/systemd/users", 0755, 0, 0);
102 if (r < 0)
103 goto finish;
104
105 r = fopen_temporary(u->state_file, &f, &temp_path);
106 if (r < 0)
107 goto finish;
108
109 fchmod(fileno(f), 0644);
110
111 fprintf(f,
112 "# This is private data. Do not parse.\n"
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
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,
146 i->sessions_by_user_next ? ' ' : '\n');
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,
155 i->sessions_by_user_next ? ' ' : '\n');
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,
164 i->sessions_by_user_next ? ' ' : '\n');
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,
172 i->sessions_by_user_next ? ' ' : '\n');
173 }
174 }
175
176 fflush(f);
177
178 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
179 r = -errno;
180 unlink(u->state_file);
181 unlink(temp_path);
182 }
183
184 fclose(f);
185 free(temp_path);
186
187 finish:
188 if (r < 0)
189 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
190
191 return r;
192 }
193
194 int user_load(User *u) {
195 int r;
196 char *display = NULL;
197 Session *s = NULL;
198
199 assert(u);
200
201 r = parse_env_file(u->state_file, NEWLINE,
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
217 if (display) {
218 s = hashmap_get(u->manager->sessions, display);
219 free(display);
220 }
221
222 if (s && s->display && display_is_local(s->display))
223 u->display = s;
224
225 return r;
226 }
227
228 static 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
262 static 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) {
279 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
280 free(p);
281 u->cgroup_path = NULL;
282 return r;
283 }
284
285 u->cgroup_path = p;
286
287 STRV_FOREACH(k, u->manager->controllers) {
288
289 if (strv_contains(u->manager->reset_controllers, *k))
290 continue;
291
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
300 static int user_start_service(User *u) {
301 assert(u);
302
303 /* FIXME: Fill me in later ... */
304
305 return 0;
306 }
307
308 int user_start(User *u) {
309 int r;
310
311 assert(u);
312
313 if (u->started)
314 return 0;
315
316 log_debug("New user %s logged in.", u->name);
317
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
335 u->started = true;
336
337 /* Save new user data */
338 user_save(u);
339
340 user_send_signal(u, true);
341
342 return 0;
343 }
344
345 static int user_stop_service(User *u) {
346 assert(u);
347
348 if (!u->service)
349 return 0;
350
351 return 0;
352 }
353
354 static int user_shall_kill(User *u) {
355 assert(u);
356
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);
367 }
368
369 static int user_terminate_cgroup(User *u) {
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
407 static int user_remove_runtime_path(User *u) {
408 int r;
409
410 assert(u);
411
412 if (!u->runtime_path)
413 return 0;
414
415 r = rm_rf(u->runtime_path, false, true, false);
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
425 int user_stop(User *u) {
426 Session *s;
427 int r = 0, k;
428 assert(u);
429
430 if (u->started)
431 log_debug("User %s logged out.", u->name);
432
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 */
445 k = user_terminate_cgroup(u);
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
454 unlink(u->state_file);
455 user_add_to_gc_queue(u);
456
457 if (u->started)
458 user_send_signal(u, false);
459
460 u->started = false;
461
462 return r;
463 }
464
465 int 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
501 int user_check_gc(User *u, bool drop_not_started) {
502 int r;
503 char *p;
504
505 assert(u);
506
507 if (drop_not_started && !u->started)
508 return 0;
509
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
534 void 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
544 UserState 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
559 int 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
577 if (pid_set)
578 set_free(pid_set);
579
580 return r;
581 }
582
583 static 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
590 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);