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