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