chiark / gitweb /
inhibit: port to sd-bus
[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(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], *slice;
342                 sprintf(lu, "%lu", (unsigned long) u->uid);
343
344                 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
345                 if (r < 0)
346                         return r;
347
348                 r = manager_start_unit(u->manager, slice, &error, &job);
349                 if (r < 0) {
350                         log_error("Failed to start user slice: %s", bus_error(&error, r));
351                         dbus_error_free(&error);
352
353                         free(slice);
354                 } else {
355                         u->slice = slice;
356
357                         free(u->slice_job);
358                         u->slice_job = job;
359                 }
360         }
361
362         if (u->slice)
363                 hashmap_put(u->manager->user_units, u->slice, u);
364
365         return 0;
366 }
367
368 static int user_start_service(User *u) {
369         DBusError error;
370         char *job;
371         int r;
372
373         assert(u);
374
375         dbus_error_init(&error);
376
377         if (!u->service) {
378                 char lu[DECIMAL_STR_MAX(unsigned long) + 1], *service;
379                 sprintf(lu, "%lu", (unsigned long) u->uid);
380
381                 service = unit_name_build("user", lu, ".service");
382                 if (!service)
383                         return log_oom();
384
385                 r = manager_start_unit(u->manager, service, &error, &job);
386                 if (r < 0) {
387                         log_error("Failed to start user service: %s", bus_error(&error, r));
388                         dbus_error_free(&error);
389
390                         free(service);
391                 } else {
392                         u->service = service;
393
394                         free(u->service_job);
395                         u->service_job = job;
396                 }
397         }
398
399         if (u->service)
400                 hashmap_put(u->manager->user_units, u->service, u);
401
402         return 0;
403 }
404
405 int user_start(User *u) {
406         int r;
407
408         assert(u);
409
410         if (u->started)
411                 return 0;
412
413         log_debug("New user %s logged in.", u->name);
414
415         /* Make XDG_RUNTIME_DIR */
416         r = user_mkdir_runtime_path(u);
417         if (r < 0)
418                 return r;
419
420         /* Create cgroup */
421         r = user_start_slice(u);
422         if (r < 0)
423                 return r;
424
425         /* Spawn user systemd */
426         r = user_start_service(u);
427         if (r < 0)
428                 return r;
429
430         if (!dual_timestamp_is_set(&u->timestamp))
431                 dual_timestamp_get(&u->timestamp);
432
433         u->started = true;
434
435         /* Save new user data */
436         user_save(u);
437
438         user_send_signal(u, true);
439
440         return 0;
441 }
442
443 static int user_stop_slice(User *u) {
444         DBusError error;
445         char *job;
446         int r;
447
448         assert(u);
449
450         dbus_error_init(&error);
451
452         if (!u->slice)
453                 return 0;
454
455         r = manager_stop_unit(u->manager, u->slice, &error, &job);
456         if (r < 0) {
457                 log_error("Failed to stop user slice: %s", bus_error(&error, r));
458                 dbus_error_free(&error);
459                 return r;
460         }
461
462         free(u->slice_job);
463         u->slice_job = job;
464
465         return r;
466 }
467
468 static int user_stop_service(User *u) {
469         DBusError error;
470         char *job;
471         int r;
472
473         assert(u);
474
475         dbus_error_init(&error);
476
477         if (!u->service)
478                 return 0;
479
480         r = manager_stop_unit(u->manager, u->service, &error, &job);
481         if (r < 0) {
482                 log_error("Failed to stop user service: %s", bus_error(&error, r));
483                 dbus_error_free(&error);
484                 return r;
485         }
486
487         free(u->service_job);
488         u->service_job = job;
489
490         return r;
491 }
492
493 static int user_remove_runtime_path(User *u) {
494         int r;
495
496         assert(u);
497
498         if (!u->runtime_path)
499                 return 0;
500
501         r = rm_rf(u->runtime_path, false, true, false);
502         if (r < 0)
503                 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
504
505         free(u->runtime_path);
506         u->runtime_path = NULL;
507
508         return r;
509 }
510
511 int user_stop(User *u) {
512         Session *s;
513         int r = 0, k;
514         assert(u);
515
516         LIST_FOREACH(sessions_by_user, s, u->sessions) {
517                 k = session_stop(s);
518                 if (k < 0)
519                         r = k;
520         }
521
522         /* Kill systemd */
523         k = user_stop_service(u);
524         if (k < 0)
525                 r = k;
526
527         /* Kill cgroup */
528         k = user_stop_slice(u);
529         if (k < 0)
530                 r = k;
531
532         user_save(u);
533
534         return r;
535 }
536
537 int user_finalize(User *u) {
538         Session *s;
539         int r = 0, k;
540
541         assert(u);
542
543         if (u->started)
544                 log_debug("User %s logged out.", u->name);
545
546         LIST_FOREACH(sessions_by_user, s, u->sessions) {
547                 k = session_finalize(s);
548                 if (k < 0)
549                         r = k;
550         }
551
552         /* Kill XDG_RUNTIME_DIR */
553         k = user_remove_runtime_path(u);
554         if (k < 0)
555                 r = k;
556
557         unlink(u->state_file);
558         user_add_to_gc_queue(u);
559
560         if (u->started) {
561                 user_send_signal(u, false);
562                 u->started = false;
563         }
564
565         return r;
566 }
567
568 int user_get_idle_hint(User *u, dual_timestamp *t) {
569         Session *s;
570         bool idle_hint = true;
571         dual_timestamp ts = { 0, 0 };
572
573         assert(u);
574
575         LIST_FOREACH(sessions_by_user, s, u->sessions) {
576                 dual_timestamp k;
577                 int ih;
578
579                 ih = session_get_idle_hint(s, &k);
580                 if (ih < 0)
581                         return ih;
582
583                 if (!ih) {
584                         if (!idle_hint) {
585                                 if (k.monotonic < ts.monotonic)
586                                         ts = k;
587                         } else {
588                                 idle_hint = false;
589                                 ts = k;
590                         }
591                 } else if (idle_hint) {
592
593                         if (k.monotonic > ts.monotonic)
594                                 ts = k;
595                 }
596         }
597
598         if (t)
599                 *t = ts;
600
601         return idle_hint;
602 }
603
604 static int user_check_linger_file(User *u) {
605         char *p;
606         int r;
607
608         if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
609                 return -ENOMEM;
610
611         r = access(p, F_OK) >= 0;
612         free(p);
613
614         return r;
615 }
616
617 int user_check_gc(User *u, bool drop_not_started) {
618         assert(u);
619
620         if (drop_not_started && !u->started)
621                 return 0;
622
623         if (u->sessions)
624                 return 1;
625
626         if (user_check_linger_file(u) > 0)
627                 return 1;
628
629         if (u->slice_job || u->service_job)
630                 return 1;
631
632         if (u->slice && manager_unit_is_active(u->manager, u->slice) != 0)
633                 return 1;
634
635         if (u->service && manager_unit_is_active(u->manager, u->service) != 0)
636                 return 1;
637
638         return 0;
639 }
640
641 void user_add_to_gc_queue(User *u) {
642         assert(u);
643
644         if (u->in_gc_queue)
645                 return;
646
647         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
648         u->in_gc_queue = true;
649 }
650
651 UserState user_get_state(User *u) {
652         Session *i;
653         bool all_closing = true;
654
655         assert(u);
656
657         if (u->closing)
658                 return USER_CLOSING;
659
660         if (u->slice_job || u->service_job)
661                 return USER_OPENING;
662
663         LIST_FOREACH(sessions_by_user, i, u->sessions) {
664                 if (session_is_active(i))
665                         return USER_ACTIVE;
666                 if (session_get_state(i) != SESSION_CLOSING)
667                         all_closing = false;
668         }
669
670         if (u->sessions)
671                 return all_closing ? USER_CLOSING : USER_ONLINE;
672
673         if (user_check_linger_file(u) > 0)
674                 return USER_LINGERING;
675
676         return USER_CLOSING;
677 }
678
679 int user_kill(User *u, int signo) {
680         assert(u);
681
682         if (!u->slice)
683                 return -ESRCH;
684
685         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
686 }
687
688 static const char* const user_state_table[_USER_STATE_MAX] = {
689         [USER_OFFLINE] = "offline",
690         [USER_OPENING] = "opening",
691         [USER_LINGERING] = "lingering",
692         [USER_ONLINE] = "online",
693         [USER_ACTIVE] = "active",
694         [USER_CLOSING] = "closing"
695 };
696
697 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);