chiark / gitweb /
2ea2067b2cecbaaf0359c442e82f026f62af40f9
[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 "fileio.h"
31 #include "path-util.h"
32 #include "special.h"
33 #include "unit-name.h"
34 #include "bus-util.h"
35 #include "bus-error.h"
36 #include "conf-parser.h"
37 #include "clean-ipc.h"
38 #include "smack-util.h"
39 #include "label.h"
40 #include "logind-user.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, UID_TO_PTR(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         free(u->runtime_path);
86
87         hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
88
89         free(u->name);
90         free(u->state_file);
91         free(u);
92 }
93
94 static int user_save_internal(User *u) {
95         _cleanup_free_ char *temp_path = NULL;
96         _cleanup_fclose_ FILE *f = NULL;
97         int r;
98
99         assert(u);
100         assert(u->state_file);
101
102         r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
103         if (r < 0)
104                 goto finish;
105
106         r = fopen_temporary(u->state_file, &f, &temp_path);
107         if (r < 0)
108                 goto finish;
109
110         fchmod(fileno(f), 0644);
111
112         fprintf(f,
113                 "# This is private data. Do not parse.\n"
114                 "NAME=%s\n"
115                 "STATE=%s\n",
116                 u->name,
117                 user_state_to_string(user_get_state(u)));
118
119         if (u->runtime_path)
120                 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
121
122         if (u->display)
123                 fprintf(f, "DISPLAY=%s\n", u->display->id);
124
125         if (dual_timestamp_is_set(&u->timestamp))
126                 fprintf(f,
127                         "REALTIME="USEC_FMT"\n"
128                         "MONOTONIC="USEC_FMT"\n",
129                         u->timestamp.realtime,
130                         u->timestamp.monotonic);
131
132         if (u->sessions) {
133                 Session *i;
134                 bool first;
135
136                 fputs("SESSIONS=", f);
137                 first = true;
138                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
139                         if (first)
140                                 first = false;
141                         else
142                                 fputc(' ', f);
143
144                         fputs(i->id, f);
145                 }
146
147                 fputs("\nSEATS=", f);
148                 first = true;
149                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
150                         if (!i->seat)
151                                 continue;
152
153                         if (first)
154                                 first = false;
155                         else
156                                 fputc(' ', f);
157
158                         fputs(i->seat->id, f);
159                 }
160
161                 fputs("\nACTIVE_SESSIONS=", f);
162                 first = true;
163                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
164                         if (!session_is_active(i))
165                                 continue;
166
167                         if (first)
168                                 first = false;
169                         else
170                                 fputc(' ', f);
171
172                         fputs(i->id, f);
173                 }
174
175                 fputs("\nONLINE_SESSIONS=", f);
176                 first = true;
177                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
178                         if (session_get_state(i) == SESSION_CLOSING)
179                                 continue;
180
181                         if (first)
182                                 first = false;
183                         else
184                                 fputc(' ', f);
185
186                         fputs(i->id, f);
187                 }
188
189                 fputs("\nACTIVE_SEATS=", f);
190                 first = true;
191                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
192                         if (!session_is_active(i) || !i->seat)
193                                 continue;
194
195                         if (first)
196                                 first = false;
197                         else
198                                 fputc(' ', f);
199
200                         fputs(i->seat->id, f);
201                 }
202
203                 fputs("\nONLINE_SEATS=", f);
204                 first = true;
205                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
206                         if (session_get_state(i) == SESSION_CLOSING || !i->seat)
207                                 continue;
208
209                         if (first)
210                                 first = false;
211                         else
212                                 fputc(' ', f);
213
214                         fputs(i->seat->id, f);
215                 }
216                 fputc('\n', f);
217         }
218
219         fflush(f);
220
221         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
222                 r = -errno;
223                 unlink(u->state_file);
224                 unlink(temp_path);
225         }
226
227 finish:
228         if (r < 0)
229                 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
230
231         return r;
232 }
233
234 int user_save(User *u) {
235         assert(u);
236
237         if (!u->started)
238                 return 0;
239
240         return user_save_internal (u);
241 }
242
243 int user_load(User *u) {
244         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
245         Session *s = NULL;
246         int r;
247
248         assert(u);
249
250         r = parse_env_file(u->state_file, NEWLINE,
251                            "RUNTIME",     &u->runtime_path,
252                            "DISPLAY",     &display,
253                            "REALTIME",    &realtime,
254                            "MONOTONIC",   &monotonic,
255                            NULL);
256         if (r < 0) {
257                 if (r == -ENOENT)
258                         return 0;
259
260                 log_error_errno(r, "Failed to read %s: %m", u->state_file);
261                 return r;
262         }
263
264         if (display)
265                 s = hashmap_get(u->manager->sessions, display);
266
267         if (s && s->display && display_is_local(s->display))
268                 u->display = s;
269
270         if (realtime) {
271                 unsigned long long l;
272                 if (sscanf(realtime, "%llu", &l) > 0)
273                         u->timestamp.realtime = l;
274         }
275
276         if (monotonic) {
277                 unsigned long long l;
278                 if (sscanf(monotonic, "%llu", &l) > 0)
279                         u->timestamp.monotonic = l;
280         }
281
282         return r;
283 }
284
285 static int user_mkdir_runtime_path(User *u) {
286         char *p;
287         int r;
288
289         assert(u);
290
291         r = mkdir_safe_label("/run/user", 0755, 0, 0);
292         if (r < 0)
293                 return log_error_errno(r, "Failed to create /run/user: %m");
294
295         if (!u->runtime_path) {
296                 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
297                         return log_oom();
298         } else
299                 p = u->runtime_path;
300
301         if (path_is_mount_point(p, 0) <= 0) {
302                 _cleanup_free_ char *t = NULL;
303
304                 (void) mkdir_label(p, 0700);
305
306                 if (mac_smack_use())
307                         r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
308                 else
309                         r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
310                 if (r < 0) {
311                         r = log_oom();
312                         goto fail;
313                 }
314
315                 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
316                 if (r < 0) {
317                         if (errno != EPERM) {
318                                 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
319                                 goto fail;
320                         }
321
322                         /* Lacking permissions, maybe
323                          * CAP_SYS_ADMIN-less container? In this case,
324                          * just use a normal directory. */
325
326                         r = chmod_and_chown(p, 0700, u->uid, u->gid);
327                         if (r < 0) {
328                                 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
329                                 goto fail;
330                         }
331                 }
332
333                 r = label_fix(p, false, false);
334                 if (r < 0)
335                         log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
336         }
337
338         u->runtime_path = p;
339         return 0;
340
341 fail:
342         if (p) {
343                 /* Try to clean up, but ignore errors */
344                 (void) rmdir(p);
345                 free(p);
346         }
347
348         u->runtime_path = NULL;
349         return r;
350 }
351
352 int user_start(User *u) {
353         int r;
354
355         assert(u);
356
357         if (u->started)
358                 return 0;
359
360         log_debug("New user %s logged in.", u->name);
361
362         /* Make XDG_RUNTIME_DIR */
363         r = user_mkdir_runtime_path(u);
364         if (r < 0)
365                 return r;
366
367         /* Save the user data so far, because pam_systemd will read the
368          * XDG_RUNTIME_DIR out of it while starting up systemd --user.
369          * We need to do user_save_internal() because we have not
370          * "officially" started yet. */
371         user_save_internal(u);
372
373         if (!dual_timestamp_is_set(&u->timestamp))
374                 dual_timestamp_get(&u->timestamp);
375
376         u->started = true;
377
378         /* Save new user data */
379         user_save(u);
380
381         user_send_signal(u, true);
382
383         return 0;
384 }
385
386 static int user_remove_runtime_path(User *u) {
387         int r;
388
389         assert(u);
390
391         if (!u->runtime_path)
392                 return 0;
393
394         r = rm_rf(u->runtime_path, false, false, false);
395         if (r < 0)
396                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
397
398         /* Ignore cases where the directory isn't mounted, as that's
399          * quite possible, if we lacked the permissions to mount
400          * something */
401         r = umount2(u->runtime_path, MNT_DETACH);
402         if (r < 0 && errno != EINVAL && errno != ENOENT)
403                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
404
405         r = rm_rf(u->runtime_path, false, true, false);
406         if (r < 0)
407                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
408
409         free(u->runtime_path);
410         u->runtime_path = NULL;
411
412         return r;
413 }
414
415 int user_stop(User *u, bool force) {
416         Session *s;
417         int r = 0, k;
418         assert(u);
419
420         /* Stop jobs have already been queued */
421         if (u->stopping) {
422                 user_save(u);
423                 return r;
424         }
425
426         LIST_FOREACH(sessions_by_user, s, u->sessions) {
427                 k = session_stop(s, force);
428                 if (k < 0)
429                         r = k;
430         }
431
432         u->stopping = true;
433
434         user_save(u);
435
436         return r;
437 }
438
439 int user_finalize(User *u) {
440         Session *s;
441         int r = 0, k;
442
443         assert(u);
444
445         if (u->started)
446                 log_debug("User %s logged out.", u->name);
447
448         LIST_FOREACH(sessions_by_user, s, u->sessions) {
449                 k = session_finalize(s);
450                 if (k < 0)
451                         r = k;
452         }
453
454         /* Kill XDG_RUNTIME_DIR */
455         k = user_remove_runtime_path(u);
456         if (k < 0)
457                 r = k;
458
459         /* Clean SysV + POSIX IPC objects */
460         if (u->manager->remove_ipc) {
461                 k = clean_ipc(u->uid);
462                 if (k < 0)
463                         r = k;
464         }
465
466         unlink(u->state_file);
467         user_add_to_gc_queue(u);
468
469         if (u->started) {
470                 user_send_signal(u, false);
471                 u->started = false;
472         }
473
474         return r;
475 }
476
477 int user_get_idle_hint(User *u, dual_timestamp *t) {
478         Session *s;
479         bool idle_hint = true;
480         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
481
482         assert(u);
483
484         LIST_FOREACH(sessions_by_user, s, u->sessions) {
485                 dual_timestamp k;
486                 int ih;
487
488                 ih = session_get_idle_hint(s, &k);
489                 if (ih < 0)
490                         return ih;
491
492                 if (!ih) {
493                         if (!idle_hint) {
494                                 if (k.monotonic < ts.monotonic)
495                                         ts = k;
496                         } else {
497                                 idle_hint = false;
498                                 ts = k;
499                         }
500                 } else if (idle_hint) {
501
502                         if (k.monotonic > ts.monotonic)
503                                 ts = k;
504                 }
505         }
506
507         if (t)
508                 *t = ts;
509
510         return idle_hint;
511 }
512
513 int user_check_linger_file(User *u) {
514         _cleanup_free_ char *cc = NULL;
515         char *p = NULL;
516
517         cc = cescape(u->name);
518         if (!cc)
519                 return -ENOMEM;
520
521         p = strjoina("/var/lib/systemd/linger/", cc);
522
523         return access(p, F_OK) >= 0;
524 }
525
526 bool user_check_gc(User *u, bool drop_not_started) {
527         assert(u);
528
529         if (drop_not_started && !u->started)
530                 return false;
531
532         if (u->sessions)
533                 return true;
534
535         if (user_check_linger_file(u) > 0)
536                 return true;
537
538         return false;
539 }
540
541 void user_add_to_gc_queue(User *u) {
542         assert(u);
543
544         if (u->in_gc_queue)
545                 return;
546
547         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
548         u->in_gc_queue = true;
549 }
550
551 UserState user_get_state(User *u) {
552         Session *i;
553
554         assert(u);
555
556         if (u->stopping)
557                 return USER_CLOSING;
558
559         if (u->sessions) {
560                 bool all_closing = true;
561
562                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
563                         SessionState state;
564
565                         state = session_get_state(i);
566                         if (state == SESSION_ACTIVE)
567                                 return USER_ACTIVE;
568                         if (state != SESSION_CLOSING)
569                                 all_closing = false;
570                 }
571
572                 return all_closing ? USER_CLOSING : USER_ONLINE;
573         }
574
575         if (user_check_linger_file(u) > 0)
576                 return USER_LINGERING;
577
578         return USER_CLOSING;
579 }
580
581 int user_kill(User *u, int signo) {
582         Session *s;
583         int res = 0;
584
585         assert(u);
586
587         LIST_FOREACH(sessions_by_user, s, u->sessions) {
588                 int r = session_kill(s, KILL_ALL, signo);
589                 if (res == 0 && r < 0)
590                         res = r;
591         }
592
593         return res;
594 }
595
596 static bool elect_display_filter(Session *s) {
597         /* Return true if the session is a candidate for the user’s ‘primary
598          * session’ or ‘display’. */
599         assert(s);
600
601         return (s->class == SESSION_USER && !s->stopping);
602 }
603
604 static int elect_display_compare(Session *s1, Session *s2) {
605         /* Indexed by SessionType. Lower numbers mean more preferred. */
606         const int type_ranks[_SESSION_TYPE_MAX] = {
607                 [SESSION_UNSPECIFIED] = 0,
608                 [SESSION_TTY] = -2,
609                 [SESSION_X11] = -3,
610                 [SESSION_WAYLAND] = -3,
611                 [SESSION_MIR] = -3,
612                 [SESSION_WEB] = -1,
613         };
614
615         /* Calculate the partial order relationship between s1 and s2,
616          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
617          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
618          * is preferred.
619          *
620          * s1 or s2 may be NULL. */
621         if (!s1 && !s2)
622                 return 0;
623
624         if ((s1 == NULL) != (s2 == NULL))
625                 return (s1 == NULL) - (s2 == NULL);
626
627         if (s1->stopping != s2->stopping)
628                 return s1->stopping - s2->stopping;
629
630         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
631                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
632
633         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
634                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
635
636         if (s1->type != s2->type)
637                 return type_ranks[s1->type] - type_ranks[s2->type];
638
639         return 0;
640 }
641
642 void user_elect_display(User *u) {
643         Session *s;
644
645         assert(u);
646
647         /* This elects a primary session for each user, which we call
648          * the "display". We try to keep the assignment stable, but we
649          * "upgrade" to better choices. */
650         log_debug("Electing new display for user %s", u->name);
651
652         LIST_FOREACH(sessions_by_user, s, u->sessions) {
653                 if (!elect_display_filter(s)) {
654                         log_debug("Ignoring session %s", s->id);
655                         continue;
656                 }
657
658                 if (elect_display_compare(s, u->display) < 0) {
659                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
660                         u->display = s;
661                 }
662         }
663 }
664
665 static const char* const user_state_table[_USER_STATE_MAX] = {
666         [USER_OFFLINE] = "offline",
667         [USER_OPENING] = "opening",
668         [USER_LINGERING] = "lingering",
669         [USER_ONLINE] = "online",
670         [USER_ACTIVE] = "active",
671         [USER_CLOSING] = "closing"
672 };
673
674 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
675
676 int config_parse_tmpfs_size(
677                 const char* unit,
678                 const char *filename,
679                 unsigned line,
680                 const char *section,
681                 unsigned section_line,
682                 const char *lvalue,
683                 int ltype,
684                 const char *rvalue,
685                 void *data,
686                 void *userdata) {
687
688         size_t *sz = data;
689         const char *e;
690         int r;
691
692         assert(filename);
693         assert(lvalue);
694         assert(rvalue);
695         assert(data);
696
697         e = endswith(rvalue, "%");
698         if (e) {
699                 unsigned long ul;
700                 char *f;
701
702                 errno = 0;
703                 ul = strtoul(rvalue, &f, 10);
704                 if (errno != 0 || f != e) {
705                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
706                         return 0;
707                 }
708
709                 if (ul <= 0 || ul >= 100) {
710                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
711                         return 0;
712                 }
713
714                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
715         } else {
716                 off_t o;
717
718                 r = parse_size(rvalue, 1024, &o);
719                 if (r < 0 || (off_t) (size_t) o != o) {
720                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
721                         return 0;
722                 }
723
724                 *sz = PAGE_ALIGN((size_t) o);
725         }
726
727         return 0;
728 }