chiark / gitweb /
readahead: avoid running of readahead services if readahead is already done
[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                 bool first;
141
142                 fputs("SESSIONS=", f);
143                 first = true;
144                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
145                         if (first)
146                                 first = false;
147                         else
148                                 fputc(' ', f);
149
150                         fputs(i->id, f);
151                 }
152
153                 fputs("\nSEATS=", f);
154                 first = true;
155                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
156                         if (!i->seat)
157                                 continue;
158
159                         if (first)
160                                 first = false;
161                         else
162                                 fputc(' ', f);
163
164                         fputs(i->seat->id, f);
165                 }
166
167                 fputs("\nACTIVE_SESSIONS=", f);
168                 first = true;
169                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
170                         if (!session_is_active(i))
171                                 continue;
172
173                         if (first)
174                                 first = false;
175                         else
176                                 fputc(' ', f);
177
178                         fputs(i->id, f);
179                 }
180
181                 fputs("\nACTIVE_SEATS=", f);
182                 first = true;
183                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
184                         if (!session_is_active(i) || !i->seat)
185                                 continue;
186
187                         if (first)
188                                 first = false;
189                         else
190                                 fputs(i->seat->id, f);
191                 }
192                 fputc('\n', f);
193         }
194
195         fflush(f);
196
197         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
198                 r = -errno;
199                 unlink(u->state_file);
200                 unlink(temp_path);
201         }
202
203         fclose(f);
204         free(temp_path);
205
206 finish:
207         if (r < 0)
208                 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
209
210         return r;
211 }
212
213 int user_load(User *u) {
214         int r;
215         char *display = NULL;
216         Session *s = NULL;
217
218         assert(u);
219
220         r = parse_env_file(u->state_file, NEWLINE,
221                            "CGROUP", &u->cgroup_path,
222                            "RUNTIME", &u->runtime_path,
223                            "SERVICE", &u->service,
224                            "DISPLAY", &display,
225                            NULL);
226         if (r < 0) {
227                 free(display);
228
229                 if (r == -ENOENT)
230                         return 0;
231
232                 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
233                 return r;
234         }
235
236         if (display) {
237                 s = hashmap_get(u->manager->sessions, display);
238                 free(display);
239         }
240
241         if (s && s->display && display_is_local(s->display))
242                 u->display = s;
243
244         return r;
245 }
246
247 static int user_mkdir_runtime_path(User *u) {
248         char *p;
249         int r;
250
251         assert(u);
252
253         r = safe_mkdir("/run/user", 0755, 0, 0);
254         if (r < 0) {
255                 log_error("Failed to create /run/user: %s", strerror(-r));
256                 return r;
257         }
258
259         if (!u->runtime_path) {
260                 p = strappend("/run/user/", u->name);
261
262                 if (!p) {
263                         log_error("Out of memory");
264                         return -ENOMEM;
265                 }
266         } else
267                 p = u->runtime_path;
268
269         r = safe_mkdir(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         return 0;
317 }
318
319 static int user_start_service(User *u) {
320         assert(u);
321
322         /* FIXME: Fill me in later ... */
323
324         return 0;
325 }
326
327 int user_start(User *u) {
328         int r;
329
330         assert(u);
331
332         if (u->started)
333                 return 0;
334
335         log_debug("New user %s logged in.", u->name);
336
337         /* Make XDG_RUNTIME_DIR */
338         r = user_mkdir_runtime_path(u);
339         if (r < 0)
340                 return r;
341
342         /* Create cgroup */
343         r = user_create_cgroup(u);
344         if (r < 0)
345                 return r;
346
347         /* Spawn user systemd */
348         r = user_start_service(u);
349         if (r < 0)
350                 return r;
351
352         dual_timestamp_get(&u->timestamp);
353
354         u->started = true;
355
356         /* Save new user data */
357         user_save(u);
358
359         user_send_signal(u, true);
360
361         return 0;
362 }
363
364 static int user_stop_service(User *u) {
365         assert(u);
366
367         if (!u->service)
368                 return 0;
369
370         return 0;
371 }
372
373 static int user_shall_kill(User *u) {
374         assert(u);
375
376         if (!u->manager->kill_user_processes)
377                 return false;
378
379         if (strv_contains(u->manager->kill_exclude_users, u->name))
380                 return false;
381
382         if (strv_isempty(u->manager->kill_only_users))
383                 return true;
384
385         return strv_contains(u->manager->kill_only_users, u->name);
386 }
387
388 static int user_terminate_cgroup(User *u) {
389         int r;
390         char **k;
391
392         assert(u);
393
394         if (!u->cgroup_path)
395                 return 0;
396
397         cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
398
399         if (user_shall_kill(u)) {
400
401                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
402                 if (r < 0)
403                         log_error("Failed to kill user cgroup: %s", strerror(-r));
404         } else {
405
406                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
407                 if (r < 0)
408                         log_error("Failed to check user cgroup: %s", strerror(-r));
409                 else if (r > 0) {
410                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
411                         if (r < 0)
412                                 log_error("Failed to delete user cgroup: %s", strerror(-r));
413                 } else
414                         r = -EBUSY;
415         }
416
417         STRV_FOREACH(k, u->manager->controllers)
418                 cg_trim(*k, u->cgroup_path, true);
419
420         free(u->cgroup_path);
421         u->cgroup_path = NULL;
422
423         return r;
424 }
425
426 static int user_remove_runtime_path(User *u) {
427         int r;
428
429         assert(u);
430
431         if (!u->runtime_path)
432                 return 0;
433
434         r = rm_rf(u->runtime_path, false, true, false);
435         if (r < 0)
436                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
437
438         free(u->runtime_path);
439         u->runtime_path = NULL;
440
441         return r;
442 }
443
444 int user_stop(User *u) {
445         Session *s;
446         int r = 0, k;
447         assert(u);
448
449         if (u->started)
450                 log_debug("User %s logged out.", u->name);
451
452         LIST_FOREACH(sessions_by_user, s, u->sessions) {
453                 k = session_stop(s);
454                 if (k < 0)
455                         r = k;
456         }
457
458         /* Kill systemd */
459         k = user_stop_service(u);
460         if (k < 0)
461                 r = k;
462
463         /* Kill cgroup */
464         k = user_terminate_cgroup(u);
465         if (k < 0)
466                 r = k;
467
468         /* Kill XDG_RUNTIME_DIR */
469         k = user_remove_runtime_path(u);
470         if (k < 0)
471                 r = k;
472
473         unlink(u->state_file);
474         user_add_to_gc_queue(u);
475
476         if (u->started)
477                 user_send_signal(u, false);
478
479         u->started = false;
480
481         return r;
482 }
483
484 int user_get_idle_hint(User *u, dual_timestamp *t) {
485         Session *s;
486         bool idle_hint = true;
487         dual_timestamp ts = { 0, 0 };
488
489         assert(u);
490
491         LIST_FOREACH(sessions_by_user, s, u->sessions) {
492                 dual_timestamp k;
493                 int ih;
494
495                 ih = session_get_idle_hint(s, &k);
496                 if (ih < 0)
497                         return ih;
498
499                 if (!ih) {
500                         if (!idle_hint) {
501                                 if (k.monotonic < ts.monotonic)
502                                         ts = k;
503                         } else {
504                                 idle_hint = false;
505                                 ts = k;
506                         }
507                 } else if (idle_hint) {
508
509                         if (k.monotonic > ts.monotonic)
510                                 ts = k;
511                 }
512         }
513
514         if (t)
515                 *t = ts;
516
517         return idle_hint;
518 }
519
520 int user_check_gc(User *u, bool drop_not_started) {
521         int r;
522         char *p;
523
524         assert(u);
525
526         if (drop_not_started && !u->started)
527                 return 0;
528
529         if (u->sessions)
530                 return 1;
531
532         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
533                 return -ENOMEM;
534
535         r = access(p, F_OK) >= 0;
536         free(p);
537
538         if (r > 0)
539                 return 1;
540
541         if (u->cgroup_path) {
542                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
543                 if (r < 0)
544                         return r;
545
546                 if (r <= 0)
547                         return 1;
548         }
549
550         return 0;
551 }
552
553 void user_add_to_gc_queue(User *u) {
554         assert(u);
555
556         if (u->in_gc_queue)
557                 return;
558
559         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
560         u->in_gc_queue = true;
561 }
562
563 UserState user_get_state(User *u) {
564         Session *i;
565
566         assert(u);
567
568         if (!u->sessions)
569                 return USER_LINGERING;
570
571         LIST_FOREACH(sessions_by_user, i, u->sessions)
572                 if (session_is_active(i))
573                         return USER_ACTIVE;
574
575         return USER_ONLINE;
576 }
577
578 int user_kill(User *u, int signo) {
579         int r = 0, q;
580         Set *pid_set = NULL;
581
582         assert(u);
583
584         if (!u->cgroup_path)
585                 return -ESRCH;
586
587         pid_set = set_new(trivial_hash_func, trivial_compare_func);
588         if (!pid_set)
589                 return -ENOMEM;
590
591         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
592         if (q < 0)
593                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
594                         r = q;
595
596         if (pid_set)
597                 set_free(pid_set);
598
599         return r;
600 }
601
602 static const char* const user_state_table[_USER_STATE_MAX] = {
603         [USER_OFFLINE] = "offline",
604         [USER_LINGERING] = "lingering",
605         [USER_ONLINE] = "online",
606         [USER_ACTIVE] = "active"
607 };
608
609 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);