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