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