]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/user-runtime-dir.c
sd-ipv4acd: fix assertion triggered when an ARP received in STARTED state
[thirdparty/systemd.git] / src / login / user-runtime-dir.c
CommitLineData
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 28static 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
63static 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
124fail:
125 /* Try to clean up, but ignore errors */
126 (void) rmdir(runtime_path);
127 return r;
128}
129
130static 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 153static 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 178static 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 201static 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
226DEFINE_MAIN_FUNCTION(run);