chiark / gitweb /
84f549479f92236af18584a5977c71228b7fdc66
[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         assert(u);
583
584         /* FIXME: No way to kill a user without systemd.  */
585         return -ESRCH;
586 }
587
588 static bool elect_display_filter(Session *s) {
589         /* Return true if the session is a candidate for the user’s ‘primary
590          * session’ or ‘display’. */
591         assert(s);
592
593         return (s->class == SESSION_USER && !s->stopping);
594 }
595
596 static int elect_display_compare(Session *s1, Session *s2) {
597         /* Indexed by SessionType. Lower numbers mean more preferred. */
598         const int type_ranks[_SESSION_TYPE_MAX] = {
599                 [SESSION_UNSPECIFIED] = 0,
600                 [SESSION_TTY] = -2,
601                 [SESSION_X11] = -3,
602                 [SESSION_WAYLAND] = -3,
603                 [SESSION_MIR] = -3,
604                 [SESSION_WEB] = -1,
605         };
606
607         /* Calculate the partial order relationship between s1 and s2,
608          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
609          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
610          * is preferred.
611          *
612          * s1 or s2 may be NULL. */
613         if (!s1 && !s2)
614                 return 0;
615
616         if ((s1 == NULL) != (s2 == NULL))
617                 return (s1 == NULL) - (s2 == NULL);
618
619         if (s1->stopping != s2->stopping)
620                 return s1->stopping - s2->stopping;
621
622         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
623                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
624
625         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
626                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
627
628         if (s1->type != s2->type)
629                 return type_ranks[s1->type] - type_ranks[s2->type];
630
631         return 0;
632 }
633
634 void user_elect_display(User *u) {
635         Session *s;
636
637         assert(u);
638
639         /* This elects a primary session for each user, which we call
640          * the "display". We try to keep the assignment stable, but we
641          * "upgrade" to better choices. */
642         log_debug("Electing new display for user %s", u->name);
643
644         LIST_FOREACH(sessions_by_user, s, u->sessions) {
645                 if (!elect_display_filter(s)) {
646                         log_debug("Ignoring session %s", s->id);
647                         continue;
648                 }
649
650                 if (elect_display_compare(s, u->display) < 0) {
651                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
652                         u->display = s;
653                 }
654         }
655 }
656
657 static const char* const user_state_table[_USER_STATE_MAX] = {
658         [USER_OFFLINE] = "offline",
659         [USER_OPENING] = "opening",
660         [USER_LINGERING] = "lingering",
661         [USER_ONLINE] = "online",
662         [USER_ACTIVE] = "active",
663         [USER_CLOSING] = "closing"
664 };
665
666 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
667
668 int config_parse_tmpfs_size(
669                 const char* unit,
670                 const char *filename,
671                 unsigned line,
672                 const char *section,
673                 unsigned section_line,
674                 const char *lvalue,
675                 int ltype,
676                 const char *rvalue,
677                 void *data,
678                 void *userdata) {
679
680         size_t *sz = data;
681         const char *e;
682         int r;
683
684         assert(filename);
685         assert(lvalue);
686         assert(rvalue);
687         assert(data);
688
689         e = endswith(rvalue, "%");
690         if (e) {
691                 unsigned long ul;
692                 char *f;
693
694                 errno = 0;
695                 ul = strtoul(rvalue, &f, 10);
696                 if (errno != 0 || f != e) {
697                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
698                         return 0;
699                 }
700
701                 if (ul <= 0 || ul >= 100) {
702                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
703                         return 0;
704                 }
705
706                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
707         } else {
708                 off_t o;
709
710                 r = parse_size(rvalue, 1024, &o);
711                 if (r < 0 || (off_t) (size_t) o != o) {
712                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
713                         return 0;
714                 }
715
716                 *sz = PAGE_ALIGN((size_t) o);
717         }
718
719         return 0;
720 }