chiark / gitweb /
8f38e58a9672c964ace425d144f50871b0ed407f
[elogind.git] / src / login / user-runtime-dir.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 //#include <stdint.h>
4 //#include <sys/mount.h>
5
6 //#include "fs-util.h"
7 //#include "label.h"
8 //#include "logind.h"
9 //#include "mkdir.h"
10 //#include "mount-util.h"
11 //#include "path-util.h"
12 //#include "rm-rf.h"
13 //#include "smack-util.h"
14 //#include "stdio-util.h"
15 //#include "string-util.h"
16 //#include "strv.h"
17 //#include "user-util.h"
18
19 static int gather_configuration(size_t *runtime_dir_size) {
20         Manager m = {};
21         int r;
22
23         manager_reset_config(&m);
24
25         r = manager_parse_config_file(&m);
26         if (r < 0)
27                 log_warning_errno(r, "Failed to parse logind.conf: %m");
28
29         *runtime_dir_size = m.runtime_dir_size;
30         return 0;
31 }
32
33 static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) {
34         int r;
35
36         assert(runtime_path);
37         assert(path_is_absolute(runtime_path));
38         assert(uid_is_valid(uid));
39         assert(gid_is_valid(gid));
40
41         r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE);
42         if (r < 0)
43                 return log_error_errno(r, "Failed to create /run/user: %m");
44
45         if (path_is_mount_point(runtime_path, NULL, 0) >= 0)
46                 log_debug("%s is already a mount point", runtime_path);
47         else {
48                 char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
49                              + DECIMAL_STR_MAX(uid_t)
50                              + DECIMAL_STR_MAX(gid_t)
51                              + DECIMAL_STR_MAX(size_t)];
52
53                 xsprintf(options,
54                          "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
55                          uid, gid, runtime_dir_size,
56                          mac_smack_use() ? ",smackfsroot=*" : "");
57
58                 (void) mkdir_label(runtime_path, 0700);
59
60                 r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
61                 if (r < 0) {
62                         if (!IN_SET(errno, EPERM, EACCES)) {
63                                 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path);
64                                 goto fail;
65                         }
66
67                         log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n"
68                                         "Assuming containerized execution, ignoring: %m", runtime_path);
69
70                         r = chmod_and_chown(runtime_path, 0700, uid, gid);
71                         if (r < 0) {
72                                 log_error_errno(r, "Failed to change ownership and mode of \"%s\": %m", runtime_path);
73                                 goto fail;
74                         }
75                 }
76
77                 r = label_fix(runtime_path, 0);
78                 if (r < 0)
79                         log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path);
80         }
81
82         return 0;
83
84 fail:
85         /* Try to clean up, but ignore errors */
86         (void) rmdir(runtime_path);
87         return r;
88 }
89
90 static int user_remove_runtime_path(const char *runtime_path) {
91         int r;
92
93         assert(runtime_path);
94         assert(path_is_absolute(runtime_path));
95
96         r = rm_rf(runtime_path, 0);
97         if (r < 0)
98                 log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", runtime_path);
99
100         /* Ignore cases where the directory isn't mounted, as that's
101          * quite possible, if we lacked the permissions to mount
102          * something */
103         r = umount2(runtime_path, MNT_DETACH);
104         if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
105                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", runtime_path);
106
107         r = rm_rf(runtime_path, REMOVE_ROOT);
108         if (r < 0)
109                 log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path);
110
111         return r;
112 }
113
114 static int do_mount(const char *runtime_path, uid_t uid, gid_t gid) {
115         size_t runtime_dir_size;
116
117         assert_se(gather_configuration(&runtime_dir_size) == 0);
118
119         log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
120         return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
121 }
122
123 static int do_umount(const char *runtime_path) {
124         log_debug("Will remove %s", runtime_path);
125         return user_remove_runtime_path(runtime_path);
126 }
127
128 int main(int argc, char *argv[]) {
129         const char *user;
130         uid_t uid;
131         gid_t gid;
132         char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
133         int r;
134
135         log_parse_environment();
136         log_open();
137
138         if (argc != 3) {
139                 log_error("This program takes two arguments.");
140                 return EXIT_FAILURE;
141         }
142         if (!STR_IN_SET(argv[1], "start", "stop")) {
143                 log_error("First argument must be either \"start\" or \"stop\".");
144                 return EXIT_FAILURE;
145         }
146
147         umask(0022);
148
149         user = argv[2];
150         r = get_user_creds(&user, &uid, &gid, NULL, NULL);
151         if (r < 0) {
152                 log_error_errno(r,
153                                 r == -ESRCH ? "No such user \"%s\"" :
154                                 r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
155                                              : "Failed to look up user \"%s\": %m",
156                                 user);
157                 return EXIT_FAILURE;
158         }
159
160         xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
161
162         if (streq(argv[1], "start"))
163                 r = do_mount(runtime_path, uid, gid);
164         else if (streq(argv[1], "stop"))
165                 r = do_umount(runtime_path);
166         else
167                 assert_not_reached("Unknown verb!");
168
169         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
170 }