chiark / gitweb /
a6672ce670acd7180bec825de3c3766cc9c1d8a3
[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                                 fputc(' ', f);
193
194                         fputs(i->seat->id, f);
195                 }
196                 fputc('\n', f);
197         }
198
199         fflush(f);
200
201         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
202                 r = -errno;
203                 unlink(u->state_file);
204                 unlink(temp_path);
205         }
206
207         fclose(f);
208         free(temp_path);
209
210 finish:
211         if (r < 0)
212                 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
213
214         return r;
215 }
216
217 int user_load(User *u) {
218         int r;
219         char *display = NULL;
220         Session *s = NULL;
221
222         assert(u);
223
224         r = parse_env_file(u->state_file, NEWLINE,
225                            "CGROUP", &u->cgroup_path,
226                            "RUNTIME", &u->runtime_path,
227                            "SERVICE", &u->service,
228                            "DISPLAY", &display,
229                            NULL);
230         if (r < 0) {
231                 free(display);
232
233                 if (r == -ENOENT)
234                         return 0;
235
236                 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
237                 return r;
238         }
239
240         if (display) {
241                 s = hashmap_get(u->manager->sessions, display);
242                 free(display);
243         }
244
245         if (s && s->display && display_is_local(s->display))
246                 u->display = s;
247
248         return r;
249 }
250
251 static int user_mkdir_runtime_path(User *u) {
252         char *p;
253         int r;
254
255         assert(u);
256
257         r = mkdir_safe_label("/run/user", 0755, 0, 0);
258         if (r < 0) {
259                 log_error("Failed to create /run/user: %s", strerror(-r));
260                 return r;
261         }
262
263         if (!u->runtime_path) {
264                 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
265                         return log_oom();
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                         return log_oom();
291         } else
292                 p = u->cgroup_path;
293
294         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
295         if (r < 0) {
296                 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
297                 free(p);
298                 u->cgroup_path = NULL;
299                 return r;
300         }
301
302         u->cgroup_path = p;
303
304         STRV_FOREACH(k, u->manager->controllers) {
305
306                 if (strv_contains(u->manager->reset_controllers, *k))
307                         continue;
308
309                 r = cg_create(*k, p);
310                 if (r < 0)
311                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
312         }
313
314         hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
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         hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
421
422         free(u->cgroup_path);
423         u->cgroup_path = NULL;
424
425         return r;
426 }
427
428 static int user_remove_runtime_path(User *u) {
429         int r;
430
431         assert(u);
432
433         if (!u->runtime_path)
434                 return 0;
435
436         r = rm_rf(u->runtime_path, false, true, false);
437         if (r < 0)
438                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
439
440         free(u->runtime_path);
441         u->runtime_path = NULL;
442
443         return r;
444 }
445
446 int user_stop(User *u) {
447         Session *s;
448         int r = 0, k;
449         assert(u);
450
451         if (u->started)
452                 log_debug("User %s logged out.", u->name);
453
454         LIST_FOREACH(sessions_by_user, s, u->sessions) {
455                 k = session_stop(s);
456                 if (k < 0)
457                         r = k;
458         }
459
460         /* Kill systemd */
461         k = user_stop_service(u);
462         if (k < 0)
463                 r = k;
464
465         /* Kill cgroup */
466         k = user_terminate_cgroup(u);
467         if (k < 0)
468                 r = k;
469
470         /* Kill XDG_RUNTIME_DIR */
471         k = user_remove_runtime_path(u);
472         if (k < 0)
473                 r = k;
474
475         unlink(u->state_file);
476         user_add_to_gc_queue(u);
477
478         if (u->started)
479                 user_send_signal(u, false);
480
481         u->started = false;
482
483         return r;
484 }
485
486 int user_get_idle_hint(User *u, dual_timestamp *t) {
487         Session *s;
488         bool idle_hint = true;
489         dual_timestamp ts = { 0, 0 };
490
491         assert(u);
492
493         LIST_FOREACH(sessions_by_user, s, u->sessions) {
494                 dual_timestamp k;
495                 int ih;
496
497                 ih = session_get_idle_hint(s, &k);
498                 if (ih < 0)
499                         return ih;
500
501                 if (!ih) {
502                         if (!idle_hint) {
503                                 if (k.monotonic < ts.monotonic)
504                                         ts = k;
505                         } else {
506                                 idle_hint = false;
507                                 ts = k;
508                         }
509                 } else if (idle_hint) {
510
511                         if (k.monotonic > ts.monotonic)
512                                 ts = k;
513                 }
514         }
515
516         if (t)
517                 *t = ts;
518
519         return idle_hint;
520 }
521
522 static int user_check_linger_file(User *u) {
523         char *p;
524         int r;
525
526         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
527                 return -ENOMEM;
528
529         r = access(p, F_OK) >= 0;
530         free(p);
531
532         return r;
533 }
534
535 int user_check_gc(User *u, bool drop_not_started) {
536         int r;
537
538         assert(u);
539
540         if (drop_not_started && !u->started)
541                 return 0;
542
543         if (u->sessions)
544                 return 1;
545
546         if (user_check_linger_file(u) > 0)
547                 return 1;
548
549         if (u->cgroup_path) {
550                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
551                 if (r < 0)
552                         return r;
553
554                 if (r <= 0)
555                         return 1;
556         }
557
558         return 0;
559 }
560
561 void user_add_to_gc_queue(User *u) {
562         assert(u);
563
564         if (u->in_gc_queue)
565                 return;
566
567         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
568         u->in_gc_queue = true;
569 }
570
571 UserState user_get_state(User *u) {
572         Session *i;
573         bool all_closing = true;
574
575         assert(u);
576
577
578         LIST_FOREACH(sessions_by_user, i, u->sessions) {
579                 if (session_is_active(i))
580                         return USER_ACTIVE;
581                 if (session_get_state(i) != SESSION_CLOSING)
582                         all_closing = false;
583         }
584
585         if (u->sessions)
586                 return all_closing ? USER_CLOSING : USER_ONLINE;
587
588         if (user_check_linger_file(u) > 0)
589                 return USER_LINGERING;
590
591         return USER_CLOSING;
592 }
593
594 int user_kill(User *u, int signo) {
595         int r = 0, q;
596         Set *pid_set = NULL;
597
598         assert(u);
599
600         if (!u->cgroup_path)
601                 return -ESRCH;
602
603         pid_set = set_new(trivial_hash_func, trivial_compare_func);
604         if (!pid_set)
605                 return -ENOMEM;
606
607         q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
608         if (q < 0)
609                 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
610                         r = q;
611
612         if (pid_set)
613                 set_free(pid_set);
614
615         return r;
616 }
617
618 static const char* const user_state_table[_USER_STATE_MAX] = {
619         [USER_OFFLINE] = "offline",
620         [USER_LINGERING] = "lingering",
621         [USER_ONLINE] = "online",
622         [USER_ACTIVE] = "active",
623         [USER_CLOSING] = "closing"
624 };
625
626 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);