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