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