chiark / gitweb /
login: pass correct boolean type to libdbus
[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 "util.h"
27 #include "mkdir.h"
28 #include "cgroup-util.h"
29 #include "hashmap.h"
30 #include "strv.h"
31 #include "fileio.h"
32 #include "special.h"
33 #include "unit-name.h"
34 #include "dbus-common.h"
35 #include "logind-user.h"
36
37 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
38         User *u;
39
40         assert(m);
41         assert(name);
42
43         u = new0(User, 1);
44         if (!u)
45                 return NULL;
46
47         u->name = strdup(name);
48         if (!u->name)
49                 goto fail;
50
51         if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
52                 goto fail;
53
54         if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
55                 goto fail;
56
57         u->manager = m;
58         u->uid = uid;
59         u->gid = gid;
60
61         return u;
62
63 fail:
64         free(u->state_file);
65         free(u->name);
66         free(u);
67
68         return NULL;
69 }
70
71 void user_free(User *u) {
72         assert(u);
73
74         if (u->in_gc_queue)
75                 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
76
77         while (u->sessions)
78                 session_free(u->sessions);
79
80         if (u->slice) {
81                 hashmap_remove(u->manager->user_units, u->slice);
82                 free(u->slice);
83         }
84
85         if (u->service) {
86                 hashmap_remove(u->manager->user_units, u->service);
87                 free(u->service);
88         }
89
90         free(u->slice_job);
91         free(u->service_job);
92
93         free(u->runtime_path);
94
95         hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
96
97         free(u->name);
98         free(u->state_file);
99         free(u);
100 }
101
102 int user_save(User *u) {
103         _cleanup_free_ char *temp_path = NULL;
104         _cleanup_fclose_ FILE *f = NULL;
105         int r;
106
107         assert(u);
108         assert(u->state_file);
109
110         if (!u->started)
111                 return 0;
112
113         r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
114         if (r < 0)
115                 goto finish;
116
117         r = fopen_temporary(u->state_file, &f, &temp_path);
118         if (r < 0)
119                 goto finish;
120
121         fchmod(fileno(f), 0644);
122
123         fprintf(f,
124                 "# This is private data. Do not parse.\n"
125                 "NAME=%s\n"
126                 "STATE=%s\n",
127                 u->name,
128                 user_state_to_string(user_get_state(u)));
129
130         if (u->runtime_path)
131                 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
132
133         if (u->service)
134                 fprintf(f, "SERVICE=%s\n", u->service);
135         if (u->service_job)
136                 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
137
138         if (u->slice)
139                 fprintf(f, "SLICE=%s\n", u->slice);
140         if (u->slice_job)
141                 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
142
143         if (u->display)
144                 fprintf(f, "DISPLAY=%s\n", u->display->id);
145
146         if (dual_timestamp_is_set(&u->timestamp))
147                 fprintf(f,
148                         "REALTIME=%llu\n"
149                         "MONOTONIC=%llu\n",
150                         (unsigned long long) u->timestamp.realtime,
151                         (unsigned long long) u->timestamp.monotonic);
152
153         if (u->sessions) {
154                 Session *i;
155                 bool first;
156
157                 fputs("SESSIONS=", f);
158                 first = true;
159                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
160                         if (first)
161                                 first = false;
162                         else
163                                 fputc(' ', f);
164
165                         fputs(i->id, f);
166                 }
167
168                 fputs("\nSEATS=", f);
169                 first = true;
170                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
171                         if (!i->seat)
172                                 continue;
173
174                         if (first)
175                                 first = false;
176                         else
177                                 fputc(' ', f);
178
179                         fputs(i->seat->id, f);
180                 }
181
182                 fputs("\nACTIVE_SESSIONS=", f);
183                 first = true;
184                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
185                         if (!session_is_active(i))
186                                 continue;
187
188                         if (first)
189                                 first = false;
190                         else
191                                 fputc(' ', f);
192
193                         fputs(i->id, f);
194                 }
195
196                 fputs("\nONLINE_SESSIONS=", f);
197                 first = true;
198                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
199                         if (session_get_state(i) == SESSION_CLOSING)
200                                 continue;
201
202                         if (first)
203                                 first = false;
204                         else
205                                 fputc(' ', f);
206
207                         fputs(i->id, f);
208                 }
209
210                 fputs("\nACTIVE_SEATS=", f);
211                 first = true;
212                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
213                         if (!session_is_active(i) || !i->seat)
214                                 continue;
215
216                         if (first)
217                                 first = false;
218                         else
219                                 fputc(' ', f);
220
221                         fputs(i->seat->id, f);
222                 }
223
224                 fputs("\nONLINE_SEATS=", f);
225                 first = true;
226                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
227                         if (session_get_state(i) == SESSION_CLOSING || !i->seat)
228                                 continue;
229
230                         if (first)
231                                 first = false;
232                         else
233                                 fputc(' ', f);
234
235                         fputs(i->seat->id, f);
236                 }
237                 fputc('\n', f);
238         }
239
240         fflush(f);
241
242         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
243                 r = -errno;
244                 unlink(u->state_file);
245                 unlink(temp_path);
246         }
247
248 finish:
249         if (r < 0)
250                 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
251
252         return r;
253 }
254
255 int user_load(User *u) {
256         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
257         Session *s = NULL;
258         int r;
259
260         assert(u);
261
262         r = parse_env_file(u->state_file, NEWLINE,
263                            "RUNTIME",     &u->runtime_path,
264                            "SERVICE",     &u->service,
265                            "SERVICE_JOB", &u->service_job,
266                            "SLICE",       &u->slice,
267                            "SLICE_JOB",   &u->slice_job,
268                            "DISPLAY",     &display,
269                            "REALTIME",    &realtime,
270                            "MONOTONIC",   &monotonic,
271                            NULL);
272         if (r < 0) {
273                 if (r == -ENOENT)
274                         return 0;
275
276                 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
277                 return r;
278         }
279
280         if (display)
281                 s = hashmap_get(u->manager->sessions, display);
282
283         if (s && s->display && display_is_local(s->display))
284                 u->display = s;
285
286         if (realtime) {
287                 unsigned long long l;
288                 if (sscanf(realtime, "%llu", &l) > 0)
289                         u->timestamp.realtime = l;
290         }
291
292         if (monotonic) {
293                 unsigned long long l;
294                 if (sscanf(monotonic, "%llu", &l) > 0)
295                         u->timestamp.monotonic = l;
296         }
297
298         return r;
299 }
300
301 static int user_mkdir_runtime_path(User *u) {
302         char *p;
303         int r;
304
305         assert(u);
306
307         r = mkdir_safe_label("/run/user", 0755, 0, 0);
308         if (r < 0) {
309                 log_error("Failed to create /run/user: %s", strerror(-r));
310                 return r;
311         }
312
313         if (!u->runtime_path) {
314                 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0)
315                         return log_oom();
316         } else
317                 p = u->runtime_path;
318
319         r = mkdir_safe_label(p, 0700, u->uid, u->gid);
320         if (r < 0) {
321                 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
322                 free(p);
323                 u->runtime_path = NULL;
324                 return r;
325         }
326
327         u->runtime_path = p;
328         return 0;
329 }
330
331 static int user_start_slice(User *u) {
332         DBusError error;
333         char *job;
334         int r;
335
336         assert(u);
337
338         dbus_error_init(&error);
339
340         if (!u->slice) {
341                 char lu[DECIMAL_STR_MAX(unsigned long) + 1];
342                 sprintf(lu, "%lu", (unsigned long) u->uid);
343
344                 r = build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
345                 if (r < 0)
346                         return r;
347
348                 r = hashmap_put(u->manager->user_units, u->slice, u);
349                 if (r < 0)
350                         log_warning("Failed to create mapping between unit and user");
351         }
352
353         r = manager_start_unit(u->manager, u->slice, &error, &job);
354         if (r < 0) {
355                 log_error("Failed to start user slice: %s", bus_error(&error, r));
356                 dbus_error_free(&error);
357         } else {
358                 free(u->slice_job);
359                 u->slice_job = job;
360         }
361
362         return 0;
363 }
364
365 static int user_start_service(User *u) {
366         DBusError error;
367         char *job;
368         int r;
369
370         assert(u);
371
372         dbus_error_init(&error);
373
374         if (!u->service) {
375                 char lu[DECIMAL_STR_MAX(unsigned long) + 1];
376                 sprintf(lu, "%lu", (unsigned long) u->uid);
377
378                 u->service = unit_name_build("user", lu, ".service");
379                 if (!u->service)
380                         return log_oom();
381
382                 r = hashmap_put(u->manager->user_units, u->service, u);
383                 if (r < 0)
384                         log_warning("Failed to create mapping between service and user");
385         }
386
387         r = manager_start_unit(u->manager, u->service, &error, &job);
388         if (r < 0) {
389                 log_error("Failed to start user service: %s", bus_error(&error, r));
390                 dbus_error_free(&error);
391         } else {
392                 free(u->service_job);
393                 u->service_job = job;
394         }
395
396         return 0;
397 }
398
399 int user_start(User *u) {
400         int r;
401
402         assert(u);
403
404         if (u->started)
405                 return 0;
406
407         log_debug("New user %s logged in.", u->name);
408
409         /* Make XDG_RUNTIME_DIR */
410         r = user_mkdir_runtime_path(u);
411         if (r < 0)
412                 return r;
413
414         /* Create cgroup */
415         r = user_start_slice(u);
416         if (r < 0)
417                 return r;
418
419         /* Spawn user systemd */
420         r = user_start_service(u);
421         if (r < 0)
422                 return r;
423
424         if (!dual_timestamp_is_set(&u->timestamp))
425                 dual_timestamp_get(&u->timestamp);
426
427         u->started = true;
428
429         /* Save new user data */
430         user_save(u);
431
432         user_send_signal(u, true);
433
434         return 0;
435 }
436
437 static int user_stop_slice(User *u) {
438         DBusError error;
439         char *job;
440         int r;
441
442         assert(u);
443
444         dbus_error_init(&error);
445
446         if (!u->slice)
447                 return 0;
448
449         r = manager_stop_unit(u->manager, u->slice, &error, &job);
450         if (r < 0) {
451                 log_error("Failed to stop user slice: %s", bus_error(&error, r));
452                 dbus_error_free(&error);
453                 return r;
454         }
455
456         free(u->slice_job);
457         u->slice_job = job;
458
459         return r;
460 }
461
462 static int user_stop_service(User *u) {
463         DBusError error;
464         char *job;
465         int r;
466
467         assert(u);
468
469         dbus_error_init(&error);
470
471         if (!u->service)
472                 return 0;
473
474         r = manager_stop_unit(u->manager, u->service, &error, &job);
475         if (r < 0) {
476                 log_error("Failed to stop user service: %s", bus_error(&error, r));
477                 dbus_error_free(&error);
478                 return r;
479         }
480
481         free(u->service_job);
482         u->service_job = job;
483
484         return r;
485 }
486
487 /* static int user_shall_kill(User *u) { */
488 /*         assert(u); */
489
490 /*         if (!u->manager->kill_user_processes) */
491 /*                 return false; */
492
493 /*         if (strv_contains(u->manager->kill_exclude_users, u->name)) */
494 /*                 return false; */
495
496 /*         if (strv_isempty(u->manager->kill_only_users)) */
497 /*                 return true; */
498
499 /*         return strv_contains(u->manager->kill_only_users, u->name); */
500 /* } */
501
502 static int user_remove_runtime_path(User *u) {
503         int r;
504
505         assert(u);
506
507         if (!u->runtime_path)
508                 return 0;
509
510         r = rm_rf(u->runtime_path, false, true, false);
511         if (r < 0)
512                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
513
514         free(u->runtime_path);
515         u->runtime_path = NULL;
516
517         return r;
518 }
519
520 int user_stop(User *u) {
521         Session *s;
522         int r = 0, k;
523         assert(u);
524
525         if (u->started)
526                 log_debug("User %s logged out.", u->name);
527
528         LIST_FOREACH(sessions_by_user, s, u->sessions) {
529                 k = session_stop(s);
530                 if (k < 0)
531                         r = k;
532         }
533
534         /* Kill systemd */
535         k = user_stop_service(u);
536         if (k < 0)
537                 r = k;
538
539         /* Kill cgroup */
540         k = user_stop_slice(u);
541         if (k < 0)
542                 r = k;
543
544         /* Kill XDG_RUNTIME_DIR */
545         k = user_remove_runtime_path(u);
546         if (k < 0)
547                 r = k;
548
549         unlink(u->state_file);
550         user_add_to_gc_queue(u);
551
552         if (u->started)
553                 user_send_signal(u, false);
554
555         u->started = false;
556
557         return r;
558 }
559
560 int user_get_idle_hint(User *u, dual_timestamp *t) {
561         Session *s;
562         bool idle_hint = true;
563         dual_timestamp ts = { 0, 0 };
564
565         assert(u);
566
567         LIST_FOREACH(sessions_by_user, s, u->sessions) {
568                 dual_timestamp k;
569                 int ih;
570
571                 ih = session_get_idle_hint(s, &k);
572                 if (ih < 0)
573                         return ih;
574
575                 if (!ih) {
576                         if (!idle_hint) {
577                                 if (k.monotonic < ts.monotonic)
578                                         ts = k;
579                         } else {
580                                 idle_hint = false;
581                                 ts = k;
582                         }
583                 } else if (idle_hint) {
584
585                         if (k.monotonic > ts.monotonic)
586                                 ts = k;
587                 }
588         }
589
590         if (t)
591                 *t = ts;
592
593         return idle_hint;
594 }
595
596 static int user_check_linger_file(User *u) {
597         char *p;
598         int r;
599
600         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
601                 return -ENOMEM;
602
603         r = access(p, F_OK) >= 0;
604         free(p);
605
606         return r;
607 }
608
609 int user_check_gc(User *u, bool drop_not_started) {
610         assert(u);
611
612         if (drop_not_started && !u->started)
613                 return 0;
614
615         if (u->sessions)
616                 return 1;
617
618         if (user_check_linger_file(u) > 0)
619                 return 1;
620
621         return 0;
622 }
623
624 void user_add_to_gc_queue(User *u) {
625         assert(u);
626
627         if (u->in_gc_queue)
628                 return;
629
630         LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
631         u->in_gc_queue = true;
632 }
633
634 UserState user_get_state(User *u) {
635         Session *i;
636         bool all_closing = true;
637
638         assert(u);
639
640         if (u->slice_job || u->service_job)
641                 return u->started ? USER_OPENING : USER_CLOSING;
642
643         LIST_FOREACH(sessions_by_user, i, u->sessions) {
644                 if (session_is_active(i))
645                         return USER_ACTIVE;
646                 if (session_get_state(i) != SESSION_CLOSING)
647                         all_closing = false;
648         }
649
650         if (u->sessions)
651                 return all_closing ? USER_CLOSING : USER_ONLINE;
652
653         if (user_check_linger_file(u) > 0)
654                 return USER_LINGERING;
655
656         return USER_CLOSING;
657 }
658
659 int user_kill(User *u, int signo) {
660         assert(u);
661
662         if (!u->slice)
663                 return -ESRCH;
664
665         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
666 }
667
668 static const char* const user_state_table[_USER_STATE_MAX] = {
669         [USER_OFFLINE] = "offline",
670         [USER_OPENING] = "opening",
671         [USER_LINGERING] = "lingering",
672         [USER_ONLINE] = "online",
673         [USER_ACTIVE] = "active",
674         [USER_CLOSING] = "closing"
675 };
676
677 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);