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