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