chiark / gitweb /
unit-name: style fix in unit_name_is_template()
[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                 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0) {
263                         log_error("Out of memory.");
264                         return -ENOMEM;
265                 }
266         } else
267                 p = u->runtime_path;
268
269         r = mkdir_safe_label(p, 0700, u->uid, u->gid);
270         if (r < 0) {
271                 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
272                 free(p);
273                 u->runtime_path = NULL;
274                 return r;
275         }
276
277         u->runtime_path = p;
278         return 0;
279 }
280
281 static int user_create_cgroup(User *u) {
282         char **k;
283         char *p;
284         int r;
285
286         assert(u);
287
288         if (!u->cgroup_path) {
289                 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
290                         log_error("Out of memory.");
291                         return -ENOMEM;
292                 }
293         } else
294                 p = u->cgroup_path;
295
296         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
297         if (r < 0) {
298                 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
299                 free(p);
300                 u->cgroup_path = NULL;
301                 return r;
302         }
303
304         u->cgroup_path = p;
305
306         STRV_FOREACH(k, u->manager->controllers) {
307
308                 if (strv_contains(u->manager->reset_controllers, *k))
309                         continue;
310
311                 r = cg_create(*k, p);
312                 if (r < 0)
313                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
314         }
315
316         hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
317
318         return 0;
319 }
320
321 static int user_start_service(User *u) {
322         assert(u);
323
324         /* FIXME: Fill me in later ... */
325
326         return 0;
327 }
328
329 int user_start(User *u) {
330         int r;
331
332         assert(u);
333
334         if (u->started)
335                 return 0;
336
337         log_debug("New user %s logged in.", u->name);
338
339         /* Make XDG_RUNTIME_DIR */
340         r = user_mkdir_runtime_path(u);
341         if (r < 0)
342                 return r;
343
344         /* Create cgroup */
345         r = user_create_cgroup(u);
346         if (r < 0)
347                 return r;
348
349         /* Spawn user systemd */
350         r = user_start_service(u);
351         if (r < 0)
352                 return r;
353
354         dual_timestamp_get(&u->timestamp);
355
356         u->started = true;
357
358         /* Save new user data */
359         user_save(u);
360
361         user_send_signal(u, true);
362
363         return 0;
364 }
365
366 static int user_stop_service(User *u) {
367         assert(u);
368
369         if (!u->service)
370                 return 0;
371
372         return 0;
373 }
374
375 static int user_shall_kill(User *u) {
376         assert(u);
377
378         if (!u->manager->kill_user_processes)
379                 return false;
380
381         if (strv_contains(u->manager->kill_exclude_users, u->name))
382                 return false;
383
384         if (strv_isempty(u->manager->kill_only_users))
385                 return true;
386
387         return strv_contains(u->manager->kill_only_users, u->name);
388 }
389
390 static int user_terminate_cgroup(User *u) {
391         int r;
392         char **k;
393
394         assert(u);
395
396         if (!u->cgroup_path)
397                 return 0;
398
399         cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
400
401         if (user_shall_kill(u)) {
402
403                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
404                 if (r < 0)
405                         log_error("Failed to kill user cgroup: %s", strerror(-r));
406         } else {
407
408                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
409                 if (r < 0)
410                         log_error("Failed to check user cgroup: %s", strerror(-r));
411                 else if (r > 0) {
412                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
413                         if (r < 0)
414                                 log_error("Failed to delete user cgroup: %s", strerror(-r));
415                 } else
416                         r = -EBUSY;
417         }
418
419         STRV_FOREACH(k, u->manager->controllers)
420                 cg_trim(*k, u->cgroup_path, true);
421
422         hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
423
424         free(u->cgroup_path);
425         u->cgroup_path = NULL;
426
427         return r;
428 }
429
430 static int user_remove_runtime_path(User *u) {
431         int r;
432
433         assert(u);
434
435         if (!u->runtime_path)
436                 return 0;
437
438         r = rm_rf(u->runtime_path, false, true, false);
439         if (r < 0)
440                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
441
442         free(u->runtime_path);
443         u->runtime_path = NULL;
444
445         return r;
446 }
447
448 int user_stop(User *u) {
449         Session *s;
450         int r = 0, k;
451         assert(u);
452
453         if (u->started)
454                 log_debug("User %s logged out.", u->name);
455
456         LIST_FOREACH(sessions_by_user, s, u->sessions) {
457                 k = session_stop(s);
458                 if (k < 0)
459                         r = k;
460         }
461
462         /* Kill systemd */
463         k = user_stop_service(u);
464         if (k < 0)
465                 r = k;
466
467         /* Kill cgroup */
468         k = user_terminate_cgroup(u);
469         if (k < 0)
470                 r = k;
471
472         /* Kill XDG_RUNTIME_DIR */
473         k = user_remove_runtime_path(u);
474         if (k < 0)
475                 r = k;
476
477         unlink(u->state_file);
478         user_add_to_gc_queue(u);
479
480         if (u->started)
481                 user_send_signal(u, false);
482
483         u->started = false;
484
485         return r;
486 }
487
488 int user_get_idle_hint(User *u, dual_timestamp *t) {
489         Session *s;
490         bool idle_hint = true;
491         dual_timestamp ts = { 0, 0 };
492
493         assert(u);
494
495         LIST_FOREACH(sessions_by_user, s, u->sessions) {
496                 dual_timestamp k;
497                 int ih;
498
499                 ih = session_get_idle_hint(s, &k);
500                 if (ih < 0)
501                         return ih;
502
503                 if (!ih) {
504                         if (!idle_hint) {
505                                 if (k.monotonic < ts.monotonic)
506                                         ts = k;
507                         } else {
508                                 idle_hint = false;
509                                 ts = k;
510                         }
511                 } else if (idle_hint) {
512
513                         if (k.monotonic > ts.monotonic)
514                                 ts = k;
515                 }
516         }
517
518         if (t)
519                 *t = ts;
520
521         return idle_hint;
522 }
523
524 static int user_check_linger_file(User *u) {
525         char *p;
526         int r;
527
528         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
529                 return -ENOMEM;
530
531         r = access(p, F_OK) >= 0;
532         free(p);
533
534         return r;
535 }
536
537 int user_check_gc(User *u, bool drop_not_started) {
538         int r;
539
540         assert(u);
541
542         if (drop_not_started && !u->started)
543                 return 0;
544
545         if (u->sessions)
546                 return 1;
547
548         if (user_check_linger_file(u) > 0)
549                 return 1;
550
551         if (u->cgroup_path) {
552                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
553                 if (r < 0)
554                         return r;
555
556                 if (r <= 0)
557                         return 1;
558         }
559
560         return 0;
561 }
562
563 void user_add_to_gc_queue(User *u) {
564         assert(u);
565
566         if (u->in_gc_queue)
567                 return;
568
569         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
570         u->in_gc_queue = true;
571 }
572
573 UserState user_get_state(User *u) {
574         Session *i;
575
576         assert(u);
577
578         LIST_FOREACH(sessions_by_user, i, u->sessions)
579                 if (session_is_active(i))
580                         return USER_ACTIVE;
581
582         if (u->sessions)
583                 return USER_ONLINE;
584
585         if (user_check_linger_file(u) > 0)
586                 return USER_LINGERING;
587
588         return USER_CLOSING;
589 }
590
591 int user_kill(User *u, int signo) {
592         int r = 0, q;
593         Set *pid_set = NULL;
594
595         assert(u);
596
597         if (!u->cgroup_path)
598                 return -ESRCH;
599
600         pid_set = set_new(trivial_hash_func, trivial_compare_func);
601         if (!pid_set)
602                 return -ENOMEM;
603
604         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
605         if (q < 0)
606                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
607                         r = q;
608
609         if (pid_set)
610                 set_free(pid_set);
611
612         return r;
613 }
614
615 static const char* const user_state_table[_USER_STATE_MAX] = {
616         [USER_OFFLINE] = "offline",
617         [USER_LINGERING] = "lingering",
618         [USER_ONLINE] = "online",
619         [USER_ACTIVE] = "active",
620         [USER_CLOSING] = "closing"
621 };
622
623 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);