]>
Commit | Line | Data |
---|---|---|
a9f0f5e5 ZJS |
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 | } |