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