chiark / gitweb /
logind: Add a two new variables to the user session tracking file.
[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("\nONLINE_SESSIONS=", f);
184                 first = true;
185                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
186                         if (session_get_state(i) == SESSION_CLOSING)
187                                 continue;
188
189                         if (first)
190                                 first = false;
191                         else
192                                 fputc(' ', f);
193
194                         fputs(i->id, f);
195                 }
196
197                 fputs("\nACTIVE_SEATS=", f);
198                 first = true;
199                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
200                         if (!session_is_active(i) || !i->seat)
201                                 continue;
202
203                         if (first)
204                                 first = false;
205                         else
206                                 fputc(' ', f);
207
208                         fputs(i->seat->id, f);
209                 }
210
211                 fputs("\nONLINE_SEATS=", f);
212                 first = true;
213                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
214                         if (session_get_state(i) == SESSION_CLOSING || !i->seat)
215                                 continue;
216
217                         if (first)
218                                 first = false;
219                         else
220                                 fputc(' ', f);
221
222                         fputs(i->seat->id, f);
223                 }
224                 fputc('\n', f);
225         }
226
227         fflush(f);
228
229         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
230                 r = -errno;
231                 unlink(u->state_file);
232                 unlink(temp_path);
233         }
234
235         fclose(f);
236         free(temp_path);
237
238 finish:
239         if (r < 0)
240                 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
241
242         return r;
243 }
244
245 int user_load(User *u) {
246         int r;
247         char *display = NULL;
248         Session *s = NULL;
249
250         assert(u);
251
252         r = parse_env_file(u->state_file, NEWLINE,
253                            "CGROUP", &u->cgroup_path,
254                            "RUNTIME", &u->runtime_path,
255                            "SERVICE", &u->service,
256                            "DISPLAY", &display,
257                            NULL);
258         if (r < 0) {
259                 free(display);
260
261                 if (r == -ENOENT)
262                         return 0;
263
264                 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
265                 return r;
266         }
267
268         if (display) {
269                 s = hashmap_get(u->manager->sessions, display);
270                 free(display);
271         }
272
273         if (s && s->display && display_is_local(s->display))
274                 u->display = s;
275
276         return r;
277 }
278
279 static int user_mkdir_runtime_path(User *u) {
280         char *p;
281         int r;
282
283         assert(u);
284
285         r = mkdir_safe_label("/run/user", 0755, 0, 0);
286         if (r < 0) {
287                 log_error("Failed to create /run/user: %s", strerror(-r));
288                 return r;
289         }
290
291         if (!u->runtime_path) {
292                 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
293                         return log_oom();
294         } else
295                 p = u->runtime_path;
296
297         r = mkdir_safe_label(p, 0700, u->uid, u->gid);
298         if (r < 0) {
299                 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
300                 free(p);
301                 u->runtime_path = NULL;
302                 return r;
303         }
304
305         u->runtime_path = p;
306         return 0;
307 }
308
309 static int user_create_cgroup(User *u) {
310         char **k;
311         char *p;
312         int r;
313
314         assert(u);
315
316         if (!u->cgroup_path) {
317                 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0)
318                         return log_oom();
319         } else
320                 p = u->cgroup_path;
321
322         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
323         if (r < 0) {
324                 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
325                 free(p);
326                 u->cgroup_path = NULL;
327                 return r;
328         }
329
330         u->cgroup_path = p;
331
332         STRV_FOREACH(k, u->manager->controllers) {
333
334                 if (strv_contains(u->manager->reset_controllers, *k))
335                         continue;
336
337                 r = cg_create(*k, p);
338                 if (r < 0)
339                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
340         }
341
342         hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
343
344         return 0;
345 }
346
347 static int user_start_service(User *u) {
348         assert(u);
349
350         /* FIXME: Fill me in later ... */
351
352         return 0;
353 }
354
355 int user_start(User *u) {
356         int r;
357
358         assert(u);
359
360         if (u->started)
361                 return 0;
362
363         log_debug("New user %s logged in.", u->name);
364
365         /* Make XDG_RUNTIME_DIR */
366         r = user_mkdir_runtime_path(u);
367         if (r < 0)
368                 return r;
369
370         /* Create cgroup */
371         r = user_create_cgroup(u);
372         if (r < 0)
373                 return r;
374
375         /* Spawn user systemd */
376         r = user_start_service(u);
377         if (r < 0)
378                 return r;
379
380         dual_timestamp_get(&u->timestamp);
381
382         u->started = true;
383
384         /* Save new user data */
385         user_save(u);
386
387         user_send_signal(u, true);
388
389         return 0;
390 }
391
392 static int user_stop_service(User *u) {
393         assert(u);
394
395         if (!u->service)
396                 return 0;
397
398         return 0;
399 }
400
401 static int user_shall_kill(User *u) {
402         assert(u);
403
404         if (!u->manager->kill_user_processes)
405                 return false;
406
407         if (strv_contains(u->manager->kill_exclude_users, u->name))
408                 return false;
409
410         if (strv_isempty(u->manager->kill_only_users))
411                 return true;
412
413         return strv_contains(u->manager->kill_only_users, u->name);
414 }
415
416 static int user_terminate_cgroup(User *u) {
417         int r;
418         char **k;
419
420         assert(u);
421
422         if (!u->cgroup_path)
423                 return 0;
424
425         cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
426
427         if (user_shall_kill(u)) {
428
429                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
430                 if (r < 0)
431                         log_error("Failed to kill user cgroup: %s", strerror(-r));
432         } else {
433
434                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
435                 if (r < 0)
436                         log_error("Failed to check user cgroup: %s", strerror(-r));
437                 else if (r > 0) {
438                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
439                         if (r < 0)
440                                 log_error("Failed to delete user cgroup: %s", strerror(-r));
441                 } else
442                         r = -EBUSY;
443         }
444
445         STRV_FOREACH(k, u->manager->controllers)
446                 cg_trim(*k, u->cgroup_path, true);
447
448         hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
449
450         free(u->cgroup_path);
451         u->cgroup_path = NULL;
452
453         return r;
454 }
455
456 static int user_remove_runtime_path(User *u) {
457         int r;
458
459         assert(u);
460
461         if (!u->runtime_path)
462                 return 0;
463
464         r = rm_rf(u->runtime_path, false, true, false);
465         if (r < 0)
466                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
467
468         free(u->runtime_path);
469         u->runtime_path = NULL;
470
471         return r;
472 }
473
474 int user_stop(User *u) {
475         Session *s;
476         int r = 0, k;
477         assert(u);
478
479         if (u->started)
480                 log_debug("User %s logged out.", u->name);
481
482         LIST_FOREACH(sessions_by_user, s, u->sessions) {
483                 k = session_stop(s);
484                 if (k < 0)
485                         r = k;
486         }
487
488         /* Kill systemd */
489         k = user_stop_service(u);
490         if (k < 0)
491                 r = k;
492
493         /* Kill cgroup */
494         k = user_terminate_cgroup(u);
495         if (k < 0)
496                 r = k;
497
498         /* Kill XDG_RUNTIME_DIR */
499         k = user_remove_runtime_path(u);
500         if (k < 0)
501                 r = k;
502
503         unlink(u->state_file);
504         user_add_to_gc_queue(u);
505
506         if (u->started)
507                 user_send_signal(u, false);
508
509         u->started = false;
510
511         return r;
512 }
513
514 int user_get_idle_hint(User *u, dual_timestamp *t) {
515         Session *s;
516         bool idle_hint = true;
517         dual_timestamp ts = { 0, 0 };
518
519         assert(u);
520
521         LIST_FOREACH(sessions_by_user, s, u->sessions) {
522                 dual_timestamp k;
523                 int ih;
524
525                 ih = session_get_idle_hint(s, &k);
526                 if (ih < 0)
527                         return ih;
528
529                 if (!ih) {
530                         if (!idle_hint) {
531                                 if (k.monotonic < ts.monotonic)
532                                         ts = k;
533                         } else {
534                                 idle_hint = false;
535                                 ts = k;
536                         }
537                 } else if (idle_hint) {
538
539                         if (k.monotonic > ts.monotonic)
540                                 ts = k;
541                 }
542         }
543
544         if (t)
545                 *t = ts;
546
547         return idle_hint;
548 }
549
550 static int user_check_linger_file(User *u) {
551         char *p;
552         int r;
553
554         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
555                 return -ENOMEM;
556
557         r = access(p, F_OK) >= 0;
558         free(p);
559
560         return r;
561 }
562
563 int user_check_gc(User *u, bool drop_not_started) {
564         int r;
565
566         assert(u);
567
568         if (drop_not_started && !u->started)
569                 return 0;
570
571         if (u->sessions)
572                 return 1;
573
574         if (user_check_linger_file(u) > 0)
575                 return 1;
576
577         if (u->cgroup_path) {
578                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
579                 if (r < 0)
580                         return r;
581
582                 if (r <= 0)
583                         return 1;
584         }
585
586         return 0;
587 }
588
589 void user_add_to_gc_queue(User *u) {
590         assert(u);
591
592         if (u->in_gc_queue)
593                 return;
594
595         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
596         u->in_gc_queue = true;
597 }
598
599 UserState user_get_state(User *u) {
600         Session *i;
601         bool all_closing = true;
602
603         assert(u);
604
605
606         LIST_FOREACH(sessions_by_user, i, u->sessions) {
607                 if (session_is_active(i))
608                         return USER_ACTIVE;
609                 if (session_get_state(i) != SESSION_CLOSING)
610                         all_closing = false;
611         }
612
613         if (u->sessions)
614                 return all_closing ? USER_CLOSING : USER_ONLINE;
615
616         if (user_check_linger_file(u) > 0)
617                 return USER_LINGERING;
618
619         return USER_CLOSING;
620 }
621
622 int user_kill(User *u, int signo) {
623         int r = 0, q;
624         Set *pid_set = NULL;
625
626         assert(u);
627
628         if (!u->cgroup_path)
629                 return -ESRCH;
630
631         pid_set = set_new(trivial_hash_func, trivial_compare_func);
632         if (!pid_set)
633                 return -ENOMEM;
634
635         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
636         if (q < 0)
637                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
638                         r = q;
639
640         if (pid_set)
641                 set_free(pid_set);
642
643         return r;
644 }
645
646 static const char* const user_state_table[_USER_STATE_MAX] = {
647         [USER_OFFLINE] = "offline",
648         [USER_LINGERING] = "lingering",
649         [USER_ONLINE] = "online",
650         [USER_ACTIVE] = "active",
651         [USER_CLOSING] = "closing"
652 };
653
654 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);