]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a9f0f5e5 ZJS |
2 | |
3 | #include <stdint.h> | |
4 | #include <sys/mount.h> | |
5 | ||
07ee5adb LP |
6 | #include "sd-bus.h" |
7 | ||
8 | #include "bus-error.h" | |
d962e737 | 9 | #include "bus-locator.h" |
e5f10caf | 10 | #include "dev-setup.h" |
ca78ad1d | 11 | #include "format-util.h" |
5d1e68b4 | 12 | #include "fs-util.h" |
0690160e | 13 | #include "label-util.h" |
5d1e68b4 | 14 | #include "limits-util.h" |
5e332028 | 15 | #include "main-func.h" |
35cd0ba5 | 16 | #include "mkdir-label.h" |
21935150 | 17 | #include "mount-util.h" |
049af8ad | 18 | #include "mountpoint-util.h" |
a9f0f5e5 ZJS |
19 | #include "path-util.h" |
20 | #include "rm-rf.h" | |
81375d80 | 21 | #include "selinux-util.h" |
a9f0f5e5 ZJS |
22 | #include "smack-util.h" |
23 | #include "stdio-util.h" | |
24 | #include "string-util.h" | |
25 | #include "strv.h" | |
26 | #include "user-util.h" | |
27 | ||
909ba690 | 28 | static int acquire_runtime_dir_properties(uint64_t *ret_size, uint64_t *ret_inodes) { |
07ee5adb | 29 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
92e31da1 | 30 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; |
909ba690 | 31 | uint64_t size, inodes; |
a9f0f5e5 ZJS |
32 | int r; |
33 | ||
909ba690 MY |
34 | assert(ret_size); |
35 | assert(ret_inodes); | |
36 | ||
07ee5adb LP |
37 | r = sd_bus_default_system(&bus); |
38 | if (r < 0) | |
39 | return log_error_errno(r, "Failed to connect to system bus: %m"); | |
a9f0f5e5 | 40 | |
909ba690 | 41 | r = bus_get_property_trivial(bus, bus_login_mgr, "RuntimeDirectorySize", &error, 't', &size); |
5d1e68b4 LP |
42 | if (r < 0) { |
43 | log_warning_errno(r, "Failed to acquire runtime directory size, ignoring: %s", bus_error_message(&error, r)); | |
909ba690 MY |
44 | sd_bus_error_free(&error); |
45 | ||
46 | size = physical_memory_scale(10U, 100U); /* 10% */ | |
5d1e68b4 | 47 | } |
a9f0f5e5 | 48 | |
909ba690 | 49 | r = bus_get_property_trivial(bus, bus_login_mgr, "RuntimeDirectoryInodesMax", &error, 't', &inodes); |
5d1e68b4 LP |
50 | if (r < 0) { |
51 | log_warning_errno(r, "Failed to acquire number of inodes for runtime directory, ignoring: %s", bus_error_message(&error, r)); | |
909ba690 MY |
52 | sd_bus_error_free(&error); |
53 | ||
54 | inodes = DIV_ROUND_UP(size, 4096); | |
5d1e68b4 | 55 | } |
cc1c85fb | 56 | |
909ba690 MY |
57 | *ret_size = size; |
58 | *ret_inodes = inodes; | |
59 | ||
a9f0f5e5 ZJS |
60 | return 0; |
61 | } | |
62 | ||
07ee5adb LP |
63 | static int user_mkdir_runtime_path( |
64 | const char *runtime_path, | |
65 | uid_t uid, | |
66 | gid_t gid, | |
cc1c85fb TM |
67 | uint64_t runtime_dir_size, |
68 | uint64_t runtime_dir_inodes) { | |
07ee5adb | 69 | |
a9f0f5e5 ZJS |
70 | int r; |
71 | ||
72 | assert(runtime_path); | |
73 | assert(path_is_absolute(runtime_path)); | |
74 | assert(uid_is_valid(uid)); | |
75 | assert(gid_is_valid(gid)); | |
76 | ||
77 | r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE); | |
78 | if (r < 0) | |
79 | return log_error_errno(r, "Failed to create /run/user: %m"); | |
80 | ||
b409aacb | 81 | if (path_is_mount_point(runtime_path) > 0) |
a9f0f5e5 ZJS |
82 | log_debug("%s is already a mount point", runtime_path); |
83 | else { | |
0b8a714b | 84 | char options[STRLEN("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*") |
a9f0f5e5 ZJS |
85 | + DECIMAL_STR_MAX(uid_t) |
86 | + DECIMAL_STR_MAX(gid_t) | |
cc1c85fb | 87 | + DECIMAL_STR_MAX(uint64_t) |
07ee5adb | 88 | + DECIMAL_STR_MAX(uint64_t)]; |
a9f0f5e5 ZJS |
89 | |
90 | xsprintf(options, | |
cc1c85fb TM |
91 | "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 ",nr_inodes=%" PRIu64 "%s", |
92 | uid, gid, runtime_dir_size, runtime_dir_inodes, | |
a9f0f5e5 ZJS |
93 | mac_smack_use() ? ",smackfsroot=*" : ""); |
94 | ||
4a00b45f ZJS |
95 | r = mkdir_label(runtime_path, 0700); |
96 | if (r < 0 && r != -EEXIST) | |
97 | return log_error_errno(r, "Failed to create %s: %m", runtime_path); | |
a9f0f5e5 | 98 | |
21935150 | 99 | r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options); |
a9f0f5e5 | 100 | if (r < 0) { |
21935150 LP |
101 | if (!ERRNO_IS_PRIVILEGE(r)) { |
102 | log_error_errno(r, "Failed to mount per-user tmpfs directory %s: %m", runtime_path); | |
a9f0f5e5 ZJS |
103 | goto fail; |
104 | } | |
105 | ||
21935150 | 106 | log_debug_errno(r, |
32429805 | 107 | "Failed to mount per-user tmpfs directory %s.\n" |
a9f0f5e5 ZJS |
108 | "Assuming containerized execution, ignoring: %m", runtime_path); |
109 | ||
110 | r = chmod_and_chown(runtime_path, 0700, uid, gid); | |
111 | if (r < 0) { | |
112 | log_error_errno(r, "Failed to change ownership and mode of \"%s\": %m", runtime_path); | |
113 | goto fail; | |
114 | } | |
115 | } | |
116 | ||
117 | r = label_fix(runtime_path, 0); | |
118 | if (r < 0) | |
119 | log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path); | |
120 | } | |
121 | ||
122 | return 0; | |
123 | ||
124 | fail: | |
125 | /* Try to clean up, but ignore errors */ | |
126 | (void) rmdir(runtime_path); | |
127 | return r; | |
128 | } | |
129 | ||
130 | static int user_remove_runtime_path(const char *runtime_path) { | |
131 | int r; | |
132 | ||
133 | assert(runtime_path); | |
134 | assert(path_is_absolute(runtime_path)); | |
135 | ||
136 | r = rm_rf(runtime_path, 0); | |
137 | if (r < 0) | |
3a13442b | 138 | log_debug_errno(r, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path); |
a9f0f5e5 | 139 | |
3a13442b LP |
140 | /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to |
141 | * mount something */ | |
a9f0f5e5 ZJS |
142 | r = umount2(runtime_path, MNT_DETACH); |
143 | if (r < 0 && !IN_SET(errno, EINVAL, ENOENT)) | |
3a13442b | 144 | log_debug_errno(errno, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path); |
a9f0f5e5 ZJS |
145 | |
146 | r = rm_rf(runtime_path, REMOVE_ROOT); | |
3a13442b LP |
147 | if (r < 0 && r != -ENOENT) |
148 | return log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path); | |
a9f0f5e5 | 149 | |
3a13442b | 150 | return 0; |
a9f0f5e5 ZJS |
151 | } |
152 | ||
86d18f3b | 153 | static int do_mount(const char *user) { |
0b8a714b | 154 | char runtime_path[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)]; |
cc1c85fb | 155 | uint64_t runtime_dir_size, runtime_dir_inodes; |
86d18f3b YW |
156 | uid_t uid; |
157 | gid_t gid; | |
158 | int r; | |
159 | ||
fafff8f1 | 160 | r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0); |
86d18f3b YW |
161 | if (r < 0) |
162 | return log_error_errno(r, | |
163 | r == -ESRCH ? "No such user \"%s\"" : | |
164 | r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group" | |
165 | : "Failed to look up user \"%s\": %m", | |
166 | user); | |
167 | ||
cc1c85fb | 168 | r = acquire_runtime_dir_properties(&runtime_dir_size, &runtime_dir_inodes); |
07ee5adb LP |
169 | if (r < 0) |
170 | return r; | |
a9f0f5e5 | 171 | |
07ee5adb | 172 | xsprintf(runtime_path, "/run/user/" UID_FMT, uid); |
a9f0f5e5 ZJS |
173 | |
174 | log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid); | |
cc1c85fb | 175 | return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size, runtime_dir_inodes); |
a9f0f5e5 ZJS |
176 | } |
177 | ||
86d18f3b | 178 | static int do_umount(const char *user) { |
0b8a714b | 179 | char runtime_path[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)]; |
86d18f3b YW |
180 | uid_t uid; |
181 | int r; | |
182 | ||
183 | /* The user may be already removed. So, first try to parse the string by parse_uid(), | |
7802194a | 184 | * and if it fails, fall back to get_user_creds(). */ |
86d18f3b | 185 | if (parse_uid(user, &uid) < 0) { |
fafff8f1 | 186 | r = get_user_creds(&user, &uid, NULL, NULL, NULL, 0); |
86d18f3b YW |
187 | if (r < 0) |
188 | return log_error_errno(r, | |
189 | r == -ESRCH ? "No such user \"%s\"" : | |
190 | r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group" | |
191 | : "Failed to look up user \"%s\": %m", | |
192 | user); | |
193 | } | |
194 | ||
195 | xsprintf(runtime_path, "/run/user/" UID_FMT, uid); | |
196 | ||
a9f0f5e5 ZJS |
197 | log_debug("Will remove %s", runtime_path); |
198 | return user_remove_runtime_path(runtime_path); | |
199 | } | |
200 | ||
cc639ee7 | 201 | static int run(int argc, char *argv[]) { |
a9f0f5e5 ZJS |
202 | int r; |
203 | ||
aa976d87 | 204 | log_setup(); |
a9f0f5e5 | 205 | |
baaa35ad ZJS |
206 | if (argc != 3) |
207 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
208 | "This program takes two arguments."); | |
209 | if (!STR_IN_SET(argv[1], "start", "stop")) | |
210 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
211 | "First argument must be either \"start\" or \"stop\"."); | |
a9f0f5e5 | 212 | |
a9ba0e32 CG |
213 | umask(0022); |
214 | ||
a452c807 | 215 | r = mac_init(); |
cc639ee7 | 216 | if (r < 0) |
a9ba0e32 | 217 | return r; |
a9f0f5e5 | 218 | |
a9f0f5e5 | 219 | if (streq(argv[1], "start")) |
cc639ee7 ZJS |
220 | return do_mount(argv[2]); |
221 | if (streq(argv[1], "stop")) | |
222 | return do_umount(argv[2]); | |
04499a70 | 223 | assert_not_reached(); |
a9f0f5e5 | 224 | } |
cc639ee7 ZJS |
225 | |
226 | DEFINE_MAIN_FUNCTION(run); |