chiark / gitweb /
kernel-install: Clean up
[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                 _cleanup_free_ char *name = NULL, *escaped = NULL;
319
320                 if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
321                         return log_oom();
322
323                 escaped = cg_escape(name);
324                 if (!escaped)
325                         return log_oom();
326
327                 p = strjoin(u->manager->cgroup_path, "/", escaped, NULL);
328                 if (!p)
329                         return log_oom();
330         } else
331                 p = u->cgroup_path;
332
333         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p, NULL);
334         if (r < 0) {
335                 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
336                 free(p);
337                 u->cgroup_path = NULL;
338                 return r;
339         }
340
341         u->cgroup_path = p;
342
343         STRV_FOREACH(k, u->manager->controllers) {
344
345                 if (strv_contains(u->manager->reset_controllers, *k))
346                         continue;
347
348                 r = cg_create(*k, p, NULL);
349                 if (r < 0)
350                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
351         }
352
353         r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
354         if (r < 0)
355                 log_warning("Failed to create mapping between cgroup and user");
356
357         return 0;
358 }
359
360 static int user_start_service(User *u) {
361         assert(u);
362
363         /* FIXME: Fill me in later ... */
364
365         return 0;
366 }
367
368 int user_start(User *u) {
369         int r;
370
371         assert(u);
372
373         if (u->started)
374                 return 0;
375
376         log_debug("New user %s logged in.", u->name);
377
378         /* Make XDG_RUNTIME_DIR */
379         r = user_mkdir_runtime_path(u);
380         if (r < 0)
381                 return r;
382
383         /* Create cgroup */
384         r = user_create_cgroup(u);
385         if (r < 0)
386                 return r;
387
388         /* Spawn user systemd */
389         r = user_start_service(u);
390         if (r < 0)
391                 return r;
392
393         dual_timestamp_get(&u->timestamp);
394
395         u->started = true;
396
397         /* Save new user data */
398         user_save(u);
399
400         user_send_signal(u, true);
401
402         return 0;
403 }
404
405 static int user_stop_service(User *u) {
406         assert(u);
407
408         if (!u->service)
409                 return 0;
410
411         return 0;
412 }
413
414 static int user_shall_kill(User *u) {
415         assert(u);
416
417         if (!u->manager->kill_user_processes)
418                 return false;
419
420         if (strv_contains(u->manager->kill_exclude_users, u->name))
421                 return false;
422
423         if (strv_isempty(u->manager->kill_only_users))
424                 return true;
425
426         return strv_contains(u->manager->kill_only_users, u->name);
427 }
428
429 static int user_terminate_cgroup(User *u) {
430         int r;
431         char **k;
432
433         assert(u);
434
435         if (!u->cgroup_path)
436                 return 0;
437
438         cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
439
440         if (user_shall_kill(u)) {
441
442                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
443                 if (r < 0)
444                         log_error("Failed to kill user cgroup: %s", strerror(-r));
445         } else {
446
447                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
448                 if (r < 0)
449                         log_error("Failed to check user cgroup: %s", strerror(-r));
450                 else if (r > 0) {
451                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
452                         if (r < 0)
453                                 log_error("Failed to delete user cgroup: %s", strerror(-r));
454                 } else
455                         r = -EBUSY;
456         }
457
458         STRV_FOREACH(k, u->manager->controllers)
459                 cg_trim(*k, u->cgroup_path, true);
460
461         hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
462
463         free(u->cgroup_path);
464         u->cgroup_path = NULL;
465
466         return r;
467 }
468
469 static int user_remove_runtime_path(User *u) {
470         int r;
471
472         assert(u);
473
474         if (!u->runtime_path)
475                 return 0;
476
477         r = rm_rf(u->runtime_path, false, true, false);
478         if (r < 0)
479                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
480
481         free(u->runtime_path);
482         u->runtime_path = NULL;
483
484         return r;
485 }
486
487 int user_stop(User *u) {
488         Session *s;
489         int r = 0, k;
490         assert(u);
491
492         if (u->started)
493                 log_debug("User %s logged out.", u->name);
494
495         LIST_FOREACH(sessions_by_user, s, u->sessions) {
496                 k = session_stop(s);
497                 if (k < 0)
498                         r = k;
499         }
500
501         /* Kill systemd */
502         k = user_stop_service(u);
503         if (k < 0)
504                 r = k;
505
506         /* Kill cgroup */
507         k = user_terminate_cgroup(u);
508         if (k < 0)
509                 r = k;
510
511         /* Kill XDG_RUNTIME_DIR */
512         k = user_remove_runtime_path(u);
513         if (k < 0)
514                 r = k;
515
516         unlink(u->state_file);
517         user_add_to_gc_queue(u);
518
519         if (u->started)
520                 user_send_signal(u, false);
521
522         u->started = false;
523
524         return r;
525 }
526
527 int user_get_idle_hint(User *u, dual_timestamp *t) {
528         Session *s;
529         bool idle_hint = true;
530         dual_timestamp ts = { 0, 0 };
531
532         assert(u);
533
534         LIST_FOREACH(sessions_by_user, s, u->sessions) {
535                 dual_timestamp k;
536                 int ih;
537
538                 ih = session_get_idle_hint(s, &k);
539                 if (ih < 0)
540                         return ih;
541
542                 if (!ih) {
543                         if (!idle_hint) {
544                                 if (k.monotonic < ts.monotonic)
545                                         ts = k;
546                         } else {
547                                 idle_hint = false;
548                                 ts = k;
549                         }
550                 } else if (idle_hint) {
551
552                         if (k.monotonic > ts.monotonic)
553                                 ts = k;
554                 }
555         }
556
557         if (t)
558                 *t = ts;
559
560         return idle_hint;
561 }
562
563 static int user_check_linger_file(User *u) {
564         char *p;
565         int r;
566
567         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
568                 return -ENOMEM;
569
570         r = access(p, F_OK) >= 0;
571         free(p);
572
573         return r;
574 }
575
576 int user_check_gc(User *u, bool drop_not_started) {
577         int r;
578
579         assert(u);
580
581         if (drop_not_started && !u->started)
582                 return 0;
583
584         if (u->sessions)
585                 return 1;
586
587         if (user_check_linger_file(u) > 0)
588                 return 1;
589
590         if (u->cgroup_path) {
591                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
592                 if (r < 0)
593                         return r;
594
595                 if (r <= 0)
596                         return 1;
597         }
598
599         return 0;
600 }
601
602 void user_add_to_gc_queue(User *u) {
603         assert(u);
604
605         if (u->in_gc_queue)
606                 return;
607
608         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
609         u->in_gc_queue = true;
610 }
611
612 UserState user_get_state(User *u) {
613         Session *i;
614         bool all_closing = true;
615
616         assert(u);
617
618
619         LIST_FOREACH(sessions_by_user, i, u->sessions) {
620                 if (session_is_active(i))
621                         return USER_ACTIVE;
622                 if (session_get_state(i) != SESSION_CLOSING)
623                         all_closing = false;
624         }
625
626         if (u->sessions)
627                 return all_closing ? USER_CLOSING : USER_ONLINE;
628
629         if (user_check_linger_file(u) > 0)
630                 return USER_LINGERING;
631
632         return USER_CLOSING;
633 }
634
635 int user_kill(User *u, int signo) {
636         int r = 0, q;
637         Set *pid_set = NULL;
638
639         assert(u);
640
641         if (!u->cgroup_path)
642                 return -ESRCH;
643
644         pid_set = set_new(trivial_hash_func, trivial_compare_func);
645         if (!pid_set)
646                 return -ENOMEM;
647
648         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
649         if (q < 0)
650                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
651                         r = q;
652
653         if (pid_set)
654                 set_free(pid_set);
655
656         return r;
657 }
658
659 static const char* const user_state_table[_USER_STATE_MAX] = {
660         [USER_OFFLINE] = "offline",
661         [USER_LINGERING] = "lingering",
662         [USER_ONLINE] = "online",
663         [USER_ACTIVE] = "active",
664         [USER_CLOSING] = "closing"
665 };
666
667 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);