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