1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "bus-locator.h"
9 #include "devnum-util.h"
10 #include "errno-util.h"
12 #include "format-util.h"
14 #include "label-util.h"
15 #include "limits-util.h"
17 #include "main-func.h"
18 #include "missing_magic.h"
19 #include "missing_syscall.h"
20 #include "mkdir-label.h"
21 #include "mount-util.h"
22 #include "mountpoint-util.h"
23 #include "path-util.h"
24 #include "quota-util.h"
27 #include "smack-util.h"
28 #include "stat-util.h"
29 #include "stdio-util.h"
30 #include "string-util.h"
32 #include "user-util.h"
35 static int acquire_runtime_dir_properties(uint64_t *ret_size
, uint64_t *ret_inodes
) {
36 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
37 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
38 uint64_t size
, inodes
;
44 r
= sd_bus_default_system(&bus
);
46 return log_error_errno(r
, "Failed to connect to system bus: %m");
48 r
= bus_get_property_trivial(bus
, bus_login_mgr
, "RuntimeDirectorySize", &error
, 't', &size
);
50 log_warning_errno(r
, "Failed to acquire runtime directory size, ignoring: %s", bus_error_message(&error
, r
));
51 sd_bus_error_free(&error
);
53 size
= physical_memory_scale(10U, 100U); /* 10% */
56 r
= bus_get_property_trivial(bus
, bus_login_mgr
, "RuntimeDirectoryInodesMax", &error
, 't', &inodes
);
58 log_warning_errno(r
, "Failed to acquire number of inodes for runtime directory, ignoring: %s", bus_error_message(&error
, r
));
59 sd_bus_error_free(&error
);
61 inodes
= DIV_ROUND_UP(size
, 4096);
70 static int user_mkdir_runtime_path(
71 const char *runtime_path
,
74 uint64_t runtime_dir_size
,
75 uint64_t runtime_dir_inodes
) {
80 assert(path_is_absolute(runtime_path
));
81 assert(uid_is_valid(uid
));
82 assert(gid_is_valid(gid
));
84 r
= mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE
);
86 return log_error_errno(r
, "Failed to create /run/user: %m");
88 if (path_is_mount_point(runtime_path
) > 0)
89 log_debug("%s is already a mount point", runtime_path
);
91 char options
[STRLEN("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*")
92 + DECIMAL_STR_MAX(uid_t
)
93 + DECIMAL_STR_MAX(gid_t
)
94 + DECIMAL_STR_MAX(uint64_t)
95 + DECIMAL_STR_MAX(uint64_t)];
98 "mode=0700,uid=" UID_FMT
",gid=" GID_FMT
",size=%" PRIu64
",nr_inodes=%" PRIu64
"%s",
99 uid
, gid
, runtime_dir_size
, runtime_dir_inodes
,
100 mac_smack_use() ? ",smackfsroot=*" : "");
102 _cleanup_free_
char *d
= strdup(runtime_path
);
106 r
= mkdir_label(runtime_path
, 0700);
107 if (r
< 0 && r
!= -EEXIST
)
108 return log_error_errno(r
, "Failed to create %s: %m", runtime_path
);
110 _cleanup_(rmdir_and_freep
) char *destroy
= TAKE_PTR(d
); /* auto-destroy */
112 r
= mount_nofollow_verbose(LOG_DEBUG
, "tmpfs", runtime_path
, "tmpfs", MS_NODEV
|MS_NOSUID
, options
);
114 if (!ERRNO_IS_PRIVILEGE(r
))
115 return log_error_errno(r
, "Failed to mount per-user tmpfs directory %s: %m", runtime_path
);
118 "Failed to mount per-user tmpfs directory %s.\n"
119 "Assuming containerized execution, ignoring: %m", runtime_path
);
121 r
= chmod_and_chown(runtime_path
, 0700, uid
, gid
);
123 return log_error_errno(r
, "Failed to change ownership and mode of \"%s\": %m", runtime_path
);
126 destroy
= mfree(destroy
); /* deactivate auto-destroy */
128 r
= label_fix(runtime_path
, 0);
130 log_warning_errno(r
, "Failed to fix label of \"%s\", ignoring: %m", runtime_path
);
136 static int do_mount(UserRecord
*ur
) {
141 if (!uid_is_valid(ur
->uid
) || !gid_is_valid(user_record_gid(ur
)))
142 return log_error_errno(SYNTHETIC_ERRNO(ENOMSG
), "User '%s' lacks UID or GID, refusing.", ur
->user_name
);
144 uint64_t runtime_dir_size
, runtime_dir_inodes
;
145 r
= acquire_runtime_dir_properties(&runtime_dir_size
, &runtime_dir_inodes
);
149 char runtime_path
[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t
)];
150 xsprintf(runtime_path
, "/run/user/" UID_FMT
, ur
->uid
);
152 log_debug("Will mount %s owned by "UID_FMT
":"GID_FMT
, runtime_path
, ur
->uid
, user_record_gid(ur
));
153 return user_mkdir_runtime_path(runtime_path
, ur
->uid
, user_record_gid(ur
), runtime_dir_size
, runtime_dir_inodes
);
156 static int user_remove_runtime_path(const char *runtime_path
) {
159 assert(runtime_path
);
160 assert(path_is_absolute(runtime_path
));
162 r
= rm_rf(runtime_path
, 0);
164 log_debug_errno(r
, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path
);
166 /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to
168 r
= RET_NERRNO(umount2(runtime_path
, MNT_DETACH
));
169 if (r
< 0 && !IN_SET(r
, -EINVAL
, -ENOENT
))
170 log_debug_errno(r
, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path
);
172 r
= rm_rf(runtime_path
, REMOVE_ROOT
);
173 if (r
< 0 && r
!= -ENOENT
)
174 return log_error_errno(r
, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path
);
179 static int do_umount(const char *user
) {
180 char runtime_path
[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t
)];
184 /* The user may be already removed. So, first try to parse the string by parse_uid(),
185 * and if it fails, fall back to get_user_creds(). */
186 if (parse_uid(user
, &uid
) < 0) {
187 r
= get_user_creds(&user
, &uid
, NULL
, NULL
, NULL
, 0);
189 return log_error_errno(r
,
190 r
== -ESRCH
? "No such user \"%s\"" :
191 r
== -ENOMSG
? "UID \"%s\" is invalid or has an invalid main group"
192 : "Failed to look up user \"%s\": %m",
196 xsprintf(runtime_path
, "/run/user/" UID_FMT
, uid
);
198 log_debug("Will remove %s", runtime_path
);
199 return user_remove_runtime_path(runtime_path
);
202 static int apply_tmpfs_quota(
208 _cleanup_set_free_ Set
*processed
= NULL
;
211 assert(uid_is_valid(uid
));
213 STRV_FOREACH(p
, paths
) {
214 _cleanup_close_
int fd
= open(*p
, O_DIRECTORY
|O_CLOEXEC
);
216 log_warning_errno(errno
, "Failed to open '%s' in order to set quota, ignoring: %m", *p
);
221 if (fstat(fd
, &st
) < 0) {
222 log_warning_errno(errno
, "Failed to stat '%s' in order to set quota, ignoring: %m", *p
);
226 /* Cover for bind mounted or symlinked /var/tmp/ + /tmp/ */
227 if (set_contains(processed
, DEVNUM_TO_PTR(st
.st_dev
))) {
228 log_debug("Not setting quota on '%s', since already processed.", *p
);
232 /* Remember we already dealt with this fs, even if the subsequent operation fails, since
233 * there's no point in appyling quota twice, regardless if it succeeds or not. */
234 if (set_ensure_put(&processed
, /* hash_ops= */ NULL
, DEVNUM_TO_PTR(st
.st_dev
)) < 0)
238 if (fstatfs(fd
, &sfs
) < 0) {
239 log_warning_errno(errno
, "Failed to statfs '%s' in order to set quota, ignoring: %m", *p
);
243 if (!is_fs_type(&sfs
, TMPFS_MAGIC
)) {
244 log_debug("Not setting quota on '%s', since not tmpfs.", *p
);
249 r
= RET_NERRNO(quotactl_fd(fd
, QCMD_FIXED(Q_GETQUOTA
, USRQUOTA
), uid
, &req
));
252 else if (ERRNO_IS_NEG_NOT_SUPPORTED(r
)) {
253 log_debug_errno(r
, "No UID quota support on %s, not setting quota: %m", *p
);
255 } else if (ERRNO_IS_NEG_PRIVILEGE(r
)) {
256 log_debug_errno(r
, "Lacking privileges to query UID quota on %s, not setting quota: %m", *p
);
259 log_warning_errno(r
, "Failed to query disk quota on %s for UID " UID_FMT
", ignoring: %m", *p
, uid
);
265 (scale
== UINT32_MAX
) ? UINT64_MAX
:
266 (uint64_t) ((double) (sfs
.f_blocks
* sfs
.f_frsize
) * scale
/ UINT32_MAX
);
271 if (FLAGS_SET(req
.dqb_valid
, QIF_BLIMITS
) && v
== req
.dqb_bhardlimit
) {
272 /* Shortcut things if everything is set up properly already */
273 log_debug("Configured quota on '%s' already matches the intended setting, not updating quota.", *p
);
277 req
.dqb_valid
= QIF_BLIMITS
;
278 req
.dqb_bsoftlimit
= req
.dqb_bhardlimit
= v
;
280 r
= RET_NERRNO(quotactl_fd(fd
, QCMD_FIXED(Q_SETQUOTA
, USRQUOTA
), uid
, &req
));
282 log_debug_errno(r
, "Not setting UID quota on %s since UID quota is not supported: %m", *p
);
284 } else if (ERRNO_IS_NEG_PRIVILEGE(r
)) {
285 log_debug_errno(r
, "Lacking privileges to set UID quota on %s, skipping: %m", *p
);
288 log_warning_errno(r
, "Failed to set disk quota limit to '%s' on %s for UID " UID_FMT
", ignoring: %m", FORMAT_BYTES(v
), *p
, uid
);
292 log_info("Successfully configured disk quota for UID " UID_FMT
" on %s to %s", uid
, *p
, FORMAT_BYTES(v
* QIF_DQBLKSIZE
));
298 static int do_tmpfs_quota(UserRecord
*ur
) {
303 if (user_record_is_root(ur
)) {
304 log_debug("Not applying tmpfs quota to root user.");
308 if (!uid_is_valid(ur
->uid
))
309 return log_error_errno(SYNTHETIC_ERRNO(ENOMSG
), "User '%s' lacks UID, refusing.", ur
->user_name
);
311 r
= apply_tmpfs_quota(STRV_MAKE("/tmp", "/var/tmp"), ur
->uid
, ur
->tmp_limit
.limit
, user_record_tmp_limit_scale(ur
));
315 r
= apply_tmpfs_quota(STRV_MAKE("/dev/shm"), ur
->uid
, ur
->dev_shm_limit
.limit
, user_record_dev_shm_limit_scale(ur
));
322 static int run(int argc
, char *argv
[]) {
328 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
329 "This program takes two arguments.");
331 const char *verb
= argv
[1], *user
= argv
[2];
333 if (!STR_IN_SET(verb
, "start", "stop"))
334 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
335 "First argument must be either \"start\" or \"stop\".");
343 if (streq(verb
, "start")) {
344 _cleanup_(user_record_unrefp
) UserRecord
*ur
= NULL
;
345 r
= userdb_by_name(user
, /* match= */ NULL
, USERDB_PARSE_NUMERIC
|USERDB_SUPPRESS_SHADOW
, &ur
);
347 return log_error_errno(r
, "User '%s' does not exist: %m", user
);
349 return log_error_errno(r
, "Failed to resolve user '%s': %m", user
);
351 /* We do two things here: mount the per-user XDG_RUNTIME_DIR, and set up tmpfs quota on /tmp/
355 RET_GATHER(r
, do_mount(ur
));
356 RET_GATHER(r
, do_tmpfs_quota(ur
));
360 if (streq(verb
, "stop"))
361 return do_umount(user
);
363 assert_not_reached();
366 DEFINE_MAIN_FUNCTION(run
);