chiark / gitweb /
logind: check return value, log warning on error
[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("\nONLINE_SESSIONS=", f);
184                 first = true;
185                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
186                         if (session_get_state(i) == SESSION_CLOSING)
187                                 continue;
188
189                         if (first)
190                                 first = false;
191                         else
192                                 fputc(' ', f);
193
194                         fputs(i->id, f);
195                 }
196
197                 fputs("\nACTIVE_SEATS=", f);
198                 first = true;
199                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
200                         if (!session_is_active(i) || !i->seat)
201                                 continue;
202
203                         if (first)
204                                 first = false;
205                         else
206                                 fputc(' ', f);
207
208                         fputs(i->seat->id, f);
209                 }
210
211                 fputs("\nONLINE_SEATS=", f);
212                 first = true;
213                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
214                         if (session_get_state(i) == SESSION_CLOSING || !i->seat)
215                                 continue;
216
217                         if (first)
218                                 first = false;
219                         else
220                                 fputc(' ', f);
221
222                         fputs(i->seat->id, f);
223                 }
224                 fputc('\n', f);
225         }
226
227         fflush(f);
228
229         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
230                 r = -errno;
231                 unlink(u->state_file);
232                 unlink(temp_path);
233         }
234
235         fclose(f);
236         free(temp_path);
237
238 finish:
239         if (r < 0)
240                 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
241
242         return r;
243 }
244
245 int user_load(User *u) {
246         int r;
247         char *display = NULL;
248         Session *s = NULL;
249
250         assert(u);
251
252         r = parse_env_file(u->state_file, NEWLINE,
253                            "CGROUP", &u->cgroup_path,
254                            "RUNTIME", &u->runtime_path,
255                            "SERVICE", &u->service,
256                            "DISPLAY", &display,
257                            NULL);
258         if (r < 0) {
259                 free(display);
260
261                 if (r == -ENOENT)
262                         return 0;
263
264                 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
265                 return r;
266         }
267
268         if (display) {
269                 s = hashmap_get(u->manager->sessions, display);
270                 free(display);
271         }
272
273         if (s && s->display && display_is_local(s->display))
274                 u->display = s;
275
276         return r;
277 }
278
279 static int user_mkdir_runtime_path(User *u) {
280         char *p;
281         int r;
282
283         assert(u);
284
285         r = mkdir_safe_label("/run/user", 0755, 0, 0);
286         if (r < 0) {
287                 log_error("Failed to create /run/user: %s", strerror(-r));
288                 return r;
289         }
290
291         if (!u->runtime_path) {
292                 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
293                         return log_oom();
294         } else
295                 p = u->runtime_path;
296
297         r = mkdir_safe_label(p, 0700, u->uid, u->gid);
298         if (r < 0) {
299                 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
300                 free(p);
301                 u->runtime_path = NULL;
302                 return r;
303         }
304
305         u->runtime_path = p;
306         return 0;
307 }
308
309 static int user_create_cgroup(User *u) {
310         char **k;
311         char *p;
312         int r;
313
314         assert(u);
315
316         if (!u->cgroup_path) {
317                 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
318                         return log_oom();
319         } else
320                 p = u->cgroup_path;
321
322         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
323         if (r < 0) {
324                 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
325                 free(p);
326                 u->cgroup_path = NULL;
327                 return r;
328         }
329
330         u->cgroup_path = p;
331
332         STRV_FOREACH(k, u->manager->controllers) {
333
334                 if (strv_contains(u->manager->reset_controllers, *k))
335                         continue;
336
337                 r = cg_create(*k, p);
338                 if (r < 0)
339                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
340         }
341
342         r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
343         if (r < 0)
344                 log_warning("Failed to create mapping between cgroup and user");
345
346         return 0;
347 }
348
349 static int user_start_service(User *u) {
350         assert(u);
351
352         /* FIXME: Fill me in later ... */
353
354         return 0;
355 }
356
357 int user_start(User *u) {
358         int r;
359
360         assert(u);
361
362         if (u->started)
363                 return 0;
364
365         log_debug("New user %s logged in.", u->name);
366
367         /* Make XDG_RUNTIME_DIR */
368         r = user_mkdir_runtime_path(u);
369         if (r < 0)
370                 return r;
371
372         /* Create cgroup */
373         r = user_create_cgroup(u);
374         if (r < 0)
375                 return r;
376
377         /* Spawn user systemd */
378         r = user_start_service(u);
379         if (r < 0)
380                 return r;
381
382         dual_timestamp_get(&u->timestamp);
383
384         u->started = true;
385
386         /* Save new user data */
387         user_save(u);
388
389         user_send_signal(u, true);
390
391         return 0;
392 }
393
394 static int user_stop_service(User *u) {
395         assert(u);
396
397         if (!u->service)
398                 return 0;
399
400         return 0;
401 }
402
403 static int user_shall_kill(User *u) {
404         assert(u);
405
406         if (!u->manager->kill_user_processes)
407                 return false;
408
409         if (strv_contains(u->manager->kill_exclude_users, u->name))
410                 return false;
411
412         if (strv_isempty(u->manager->kill_only_users))
413                 return true;
414
415         return strv_contains(u->manager->kill_only_users, u->name);
416 }
417
418 static int user_terminate_cgroup(User *u) {
419         int r;
420         char **k;
421
422         assert(u);
423
424         if (!u->cgroup_path)
425                 return 0;
426
427         cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
428
429         if (user_shall_kill(u)) {
430
431                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
432                 if (r < 0)
433                         log_error("Failed to kill user cgroup: %s", strerror(-r));
434         } else {
435
436                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
437                 if (r < 0)
438                         log_error("Failed to check user cgroup: %s", strerror(-r));
439                 else if (r > 0) {
440                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
441                         if (r < 0)
442                                 log_error("Failed to delete user cgroup: %s", strerror(-r));
443                 } else
444                         r = -EBUSY;
445         }
446
447         STRV_FOREACH(k, u->manager->controllers)
448                 cg_trim(*k, u->cgroup_path, true);
449
450         hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
451
452         free(u->cgroup_path);
453         u->cgroup_path = NULL;
454
455         return r;
456 }
457
458 static int user_remove_runtime_path(User *u) {
459         int r;
460
461         assert(u);
462
463         if (!u->runtime_path)
464                 return 0;
465
466         r = rm_rf(u->runtime_path, false, true, false);
467         if (r < 0)
468                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
469
470         free(u->runtime_path);
471         u->runtime_path = NULL;
472
473         return r;
474 }
475
476 int user_stop(User *u) {
477         Session *s;
478         int r = 0, k;
479         assert(u);
480
481         if (u->started)
482                 log_debug("User %s logged out.", u->name);
483
484         LIST_FOREACH(sessions_by_user, s, u->sessions) {
485                 k = session_stop(s);
486                 if (k < 0)
487                         r = k;
488         }
489
490         /* Kill systemd */
491         k = user_stop_service(u);
492         if (k < 0)
493                 r = k;
494
495         /* Kill cgroup */
496         k = user_terminate_cgroup(u);
497         if (k < 0)
498                 r = k;
499
500         /* Kill XDG_RUNTIME_DIR */
501         k = user_remove_runtime_path(u);
502         if (k < 0)
503                 r = k;
504
505         unlink(u->state_file);
506         user_add_to_gc_queue(u);
507
508         if (u->started)
509                 user_send_signal(u, false);
510
511         u->started = false;
512
513         return r;
514 }
515
516 int user_get_idle_hint(User *u, dual_timestamp *t) {
517         Session *s;
518         bool idle_hint = true;
519         dual_timestamp ts = { 0, 0 };
520
521         assert(u);
522
523         LIST_FOREACH(sessions_by_user, s, u->sessions) {
524                 dual_timestamp k;
525                 int ih;
526
527                 ih = session_get_idle_hint(s, &k);
528                 if (ih < 0)
529                         return ih;
530
531                 if (!ih) {
532                         if (!idle_hint) {
533                                 if (k.monotonic < ts.monotonic)
534                                         ts = k;
535                         } else {
536                                 idle_hint = false;
537                                 ts = k;
538                         }
539                 } else if (idle_hint) {
540
541                         if (k.monotonic > ts.monotonic)
542                                 ts = k;
543                 }
544         }
545
546         if (t)
547                 *t = ts;
548
549         return idle_hint;
550 }
551
552 static int user_check_linger_file(User *u) {
553         char *p;
554         int r;
555
556         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
557                 return -ENOMEM;
558
559         r = access(p, F_OK) >= 0;
560         free(p);
561
562         return r;
563 }
564
565 int user_check_gc(User *u, bool drop_not_started) {
566         int r;
567
568         assert(u);
569
570         if (drop_not_started && !u->started)
571                 return 0;
572
573         if (u->sessions)
574                 return 1;
575
576         if (user_check_linger_file(u) > 0)
577                 return 1;
578
579         if (u->cgroup_path) {
580                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
581                 if (r < 0)
582                         return r;
583
584                 if (r <= 0)
585                         return 1;
586         }
587
588         return 0;
589 }
590
591 void user_add_to_gc_queue(User *u) {
592         assert(u);
593
594         if (u->in_gc_queue)
595                 return;
596
597         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
598         u->in_gc_queue = true;
599 }
600
601 UserState user_get_state(User *u) {
602         Session *i;
603         bool all_closing = true;
604
605         assert(u);
606
607
608         LIST_FOREACH(sessions_by_user, i, u->sessions) {
609                 if (session_is_active(i))
610                         return USER_ACTIVE;
611                 if (session_get_state(i) != SESSION_CLOSING)
612                         all_closing = false;
613         }
614
615         if (u->sessions)
616                 return all_closing ? USER_CLOSING : USER_ONLINE;
617
618         if (user_check_linger_file(u) > 0)
619                 return USER_LINGERING;
620
621         return USER_CLOSING;
622 }
623
624 int user_kill(User *u, int signo) {
625         int r = 0, q;
626         Set *pid_set = NULL;
627
628         assert(u);
629
630         if (!u->cgroup_path)
631                 return -ESRCH;
632
633         pid_set = set_new(trivial_hash_func, trivial_compare_func);
634         if (!pid_set)
635                 return -ENOMEM;
636
637         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
638         if (q < 0)
639                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
640                         r = q;
641
642         if (pid_set)
643                 set_free(pid_set);
644
645         return r;
646 }
647
648 static const char* const user_state_table[_USER_STATE_MAX] = {
649         [USER_OFFLINE] = "offline",
650         [USER_LINGERING] = "lingering",
651         [USER_ONLINE] = "online",
652         [USER_ACTIVE] = "active",
653         [USER_CLOSING] = "closing"
654 };
655
656 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);