]>
Commit | Line | Data |
---|---|---|
cc73685d | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
4295c5de | 2 | |
d38dd64a CB |
3 | #ifndef _GNU_SOURCE |
4 | #define _GNU_SOURCE 1 | |
5 | #endif | |
a6f151a7 CB |
6 | #include <sys/prctl.h> |
7 | ||
d38dd64a CB |
8 | #include "compiler.h" |
9 | #include "config.h" | |
37ef15bb | 10 | #include "file_utils.h" |
4295c5de SH |
11 | #include "initutils.h" |
12 | #include "log.h" | |
b81689a1 | 13 | #include "macro.h" |
55d830d8 | 14 | #include "memory_utils.h" |
4295c5de | 15 | |
43f984ea DJ |
16 | #ifndef HAVE_STRLCPY |
17 | #include "include/strlcpy.h" | |
18 | #endif | |
19 | ||
ac2cecc4 | 20 | lxc_log_define(initutils, lxc); |
4295c5de | 21 | |
4295c5de SH |
22 | static char *copy_global_config_value(char *p) |
23 | { | |
24 | int len = strlen(p); | |
25 | char *retbuf; | |
26 | ||
27 | if (len < 1) | |
28 | return NULL; | |
43f984ea | 29 | |
4295c5de SH |
30 | if (p[len-1] == '\n') { |
31 | p[len-1] = '\0'; | |
32 | len--; | |
33 | } | |
43f984ea DJ |
34 | |
35 | retbuf = malloc(len + 1); | |
4295c5de SH |
36 | if (!retbuf) |
37 | return NULL; | |
43f984ea DJ |
38 | |
39 | (void)strlcpy(retbuf, p, len + 1); | |
4295c5de SH |
40 | return retbuf; |
41 | } | |
42 | ||
43 | const char *lxc_global_config_value(const char *option_name) | |
44 | { | |
45 | static const char * const options[][2] = { | |
46 | { "lxc.bdev.lvm.vg", DEFAULT_VG }, | |
47 | { "lxc.bdev.lvm.thin_pool", DEFAULT_THIN_POOL }, | |
48 | { "lxc.bdev.zfs.root", DEFAULT_ZFSROOT }, | |
7da812df | 49 | { "lxc.bdev.rbd.rbdpool", DEFAULT_RBDPOOL }, |
4295c5de SH |
50 | { "lxc.lxcpath", NULL }, |
51 | { "lxc.default_config", NULL }, | |
52 | { "lxc.cgroup.pattern", NULL }, | |
53 | { "lxc.cgroup.use", NULL }, | |
54 | { NULL, NULL }, | |
55 | }; | |
56 | ||
57 | /* placed in the thread local storage pool for non-bionic targets */ | |
58 | #ifdef HAVE_TLS | |
d7f19646 | 59 | static thread_local const char *values[sizeof(options) / sizeof(options[0])] = {0}; |
4295c5de | 60 | #else |
d7f19646 | 61 | static const char *values[sizeof(options) / sizeof(options[0])] = {0}; |
4295c5de SH |
62 | #endif |
63 | ||
64 | /* user_config_path is freed as soon as it is used */ | |
65 | char *user_config_path = NULL; | |
66 | ||
67 | /* | |
68 | * The following variables are freed at bottom unconditionally. | |
69 | * So NULL the value if it is to be returned to the caller | |
70 | */ | |
71 | char *user_default_config_path = NULL; | |
72 | char *user_lxc_path = NULL; | |
73 | char *user_cgroup_pattern = NULL; | |
74 | ||
75 | if (geteuid() > 0) { | |
76 | const char *user_home = getenv("HOME"); | |
77 | if (!user_home) | |
78 | user_home = "/"; | |
79 | ||
80 | user_config_path = malloc(sizeof(char) * (22 + strlen(user_home))); | |
81 | user_default_config_path = malloc(sizeof(char) * (26 + strlen(user_home))); | |
82 | user_lxc_path = malloc(sizeof(char) * (19 + strlen(user_home))); | |
83 | ||
84 | sprintf(user_config_path, "%s/.config/lxc/lxc.conf", user_home); | |
85 | sprintf(user_default_config_path, "%s/.config/lxc/default.conf", user_home); | |
86 | sprintf(user_lxc_path, "%s/.local/share/lxc/", user_home); | |
fe70edee | 87 | user_cgroup_pattern = strdup("%n"); |
4295c5de SH |
88 | } |
89 | else { | |
90 | user_config_path = strdup(LXC_GLOBAL_CONF); | |
91 | user_default_config_path = strdup(LXC_DEFAULT_CONFIG); | |
92 | user_lxc_path = strdup(LXCPATH); | |
93 | user_cgroup_pattern = strdup(DEFAULT_CGROUP_PATTERN); | |
94 | } | |
95 | ||
96 | const char * const (*ptr)[2]; | |
97 | size_t i; | |
4295c5de SH |
98 | FILE *fin = NULL; |
99 | ||
100 | for (i = 0, ptr = options; (*ptr)[0]; ptr++, i++) { | |
101 | if (!strcmp(option_name, (*ptr)[0])) | |
102 | break; | |
103 | } | |
104 | if (!(*ptr)[0]) { | |
105 | free(user_config_path); | |
106 | free(user_default_config_path); | |
107 | free(user_lxc_path); | |
108 | free(user_cgroup_pattern); | |
109 | errno = EINVAL; | |
110 | return NULL; | |
111 | } | |
112 | ||
113 | if (values[i]) { | |
114 | free(user_config_path); | |
115 | free(user_default_config_path); | |
116 | free(user_lxc_path); | |
117 | free(user_cgroup_pattern); | |
118 | return values[i]; | |
119 | } | |
120 | ||
121 | fin = fopen_cloexec(user_config_path, "r"); | |
122 | free(user_config_path); | |
123 | if (fin) { | |
55d830d8 CB |
124 | __do_free char *line = NULL; |
125 | size_t len = 0; | |
126 | char *slider1, *slider2; | |
127 | ||
128 | while (getline(&line, &len, fin) > 0) { | |
129 | if (*line == '#') | |
4295c5de | 130 | continue; |
55d830d8 CB |
131 | |
132 | slider1 = strstr(line, option_name); | |
133 | if (!slider1) | |
4295c5de | 134 | continue; |
55d830d8 | 135 | |
4295c5de SH |
136 | /* see if there was just white space in front |
137 | * of the option name | |
138 | */ | |
55d830d8 CB |
139 | for (slider2 = line; slider2 < slider1; slider2++) |
140 | if (*slider2 != ' ' && *slider2 != '\t') | |
4295c5de | 141 | break; |
55d830d8 CB |
142 | |
143 | if (slider2 < slider1) | |
4295c5de | 144 | continue; |
55d830d8 CB |
145 | |
146 | slider1 = strchr(slider1, '='); | |
147 | if (!slider1) | |
4295c5de | 148 | continue; |
55d830d8 | 149 | |
4295c5de SH |
150 | /* see if there was just white space after |
151 | * the option name | |
152 | */ | |
55d830d8 CB |
153 | for (slider2 += strlen(option_name); slider2 < slider1; |
154 | slider2++) | |
155 | if (*slider2 != ' ' && *slider2 != '\t') | |
4295c5de | 156 | break; |
55d830d8 CB |
157 | |
158 | if (slider2 < slider1) | |
4295c5de | 159 | continue; |
55d830d8 CB |
160 | |
161 | slider1++; | |
162 | while (*slider1 && (*slider1 == ' ' || *slider1 == '\t')) | |
163 | slider1++; | |
164 | ||
165 | if (!*slider1) | |
4295c5de SH |
166 | continue; |
167 | ||
168 | if (strcmp(option_name, "lxc.lxcpath") == 0) { | |
169 | free(user_lxc_path); | |
55d830d8 | 170 | user_lxc_path = copy_global_config_value(slider1); |
4295c5de SH |
171 | remove_trailing_slashes(user_lxc_path); |
172 | values[i] = user_lxc_path; | |
173 | user_lxc_path = NULL; | |
174 | goto out; | |
175 | } | |
176 | ||
55d830d8 | 177 | values[i] = copy_global_config_value(slider1); |
4295c5de SH |
178 | goto out; |
179 | } | |
180 | } | |
55d830d8 | 181 | |
4295c5de SH |
182 | /* could not find value, use default */ |
183 | if (strcmp(option_name, "lxc.lxcpath") == 0) { | |
184 | remove_trailing_slashes(user_lxc_path); | |
185 | values[i] = user_lxc_path; | |
186 | user_lxc_path = NULL; | |
187 | } | |
188 | else if (strcmp(option_name, "lxc.default_config") == 0) { | |
189 | values[i] = user_default_config_path; | |
190 | user_default_config_path = NULL; | |
191 | } | |
192 | else if (strcmp(option_name, "lxc.cgroup.pattern") == 0) { | |
193 | values[i] = user_cgroup_pattern; | |
194 | user_cgroup_pattern = NULL; | |
195 | } | |
196 | else | |
197 | values[i] = (*ptr)[1]; | |
198 | ||
199 | /* special case: if default value is NULL, | |
200 | * and there is no config, don't view that | |
201 | * as an error... */ | |
202 | if (!values[i]) | |
203 | errno = 0; | |
204 | ||
205 | out: | |
206 | if (fin) | |
207 | fclose(fin); | |
208 | ||
209 | free(user_cgroup_pattern); | |
210 | free(user_default_config_path); | |
211 | free(user_lxc_path); | |
212 | ||
213 | return values[i]; | |
214 | } | |
215 | ||
a6f151a7 CB |
216 | /* |
217 | * Sets the process title to the specified title. Note that this may fail if | |
218 | * the kernel doesn't support PR_SET_MM_MAP (kernels <3.18). | |
219 | */ | |
220 | int setproctitle(char *title) | |
221 | { | |
f1170ed2 CB |
222 | __do_fclose FILE *f = NULL; |
223 | int i, fd, len; | |
e1d43053 | 224 | char *buf_ptr, *tmp_proctitle; |
f1170ed2 CB |
225 | char buf[LXC_LINELEN]; |
226 | int ret = 0; | |
227 | ssize_t bytes_read = 0; | |
a6f151a7 | 228 | static char *proctitle = NULL; |
a6f151a7 | 229 | |
f1170ed2 CB |
230 | /* |
231 | * We don't really need to know all of this stuff, but unfortunately | |
a6f151a7 CB |
232 | * PR_SET_MM_MAP requires us to set it all at once, so we have to |
233 | * figure it out anyway. | |
234 | */ | |
235 | unsigned long start_data, end_data, start_brk, start_code, end_code, | |
f1170ed2 | 236 | start_stack, arg_start, arg_end, env_start, env_end, brk_val; |
a6f151a7 CB |
237 | struct prctl_mm_map prctl_map; |
238 | ||
239 | f = fopen_cloexec("/proc/self/stat", "r"); | |
f1170ed2 | 240 | if (!f) |
a6f151a7 | 241 | return -1; |
a6f151a7 | 242 | |
f1170ed2 CB |
243 | fd = fileno(f); |
244 | if (fd < 0) | |
a6f151a7 | 245 | return -1; |
f1170ed2 CB |
246 | |
247 | bytes_read = lxc_read_nointr(fd, buf, sizeof(buf) - 1); | |
248 | if (bytes_read <= 0) | |
249 | return -1; | |
250 | ||
251 | buf[bytes_read] = '\0'; | |
a6f151a7 CB |
252 | |
253 | /* Skip the first 25 fields, column 26-28 are start_code, end_code, | |
254 | * and start_stack */ | |
f1170ed2 | 255 | buf_ptr = strchr(buf, ' '); |
a6f151a7 | 256 | for (i = 0; i < 24; i++) { |
f1170ed2 | 257 | if (!buf_ptr) |
a6f151a7 | 258 | return -1; |
f1170ed2 | 259 | buf_ptr = strchr(buf_ptr + 1, ' '); |
a6f151a7 | 260 | } |
f1170ed2 | 261 | if (!buf_ptr) |
a6f151a7 CB |
262 | return -1; |
263 | ||
f1170ed2 | 264 | i = sscanf(buf_ptr, "%lu %lu %lu", &start_code, &end_code, &start_stack); |
a6f151a7 CB |
265 | if (i != 3) |
266 | return -1; | |
267 | ||
268 | /* Skip the next 19 fields, column 45-51 are start_data to arg_end */ | |
269 | for (i = 0; i < 19; i++) { | |
f1170ed2 | 270 | if (!buf_ptr) |
a6f151a7 | 271 | return -1; |
f1170ed2 | 272 | buf_ptr = strchr(buf_ptr + 1, ' '); |
a6f151a7 CB |
273 | } |
274 | ||
f1170ed2 | 275 | if (!buf_ptr) |
a6f151a7 CB |
276 | return -1; |
277 | ||
f1170ed2 CB |
278 | i = sscanf(buf_ptr, "%lu %lu %lu %*u %*u %lu %lu", &start_data, |
279 | &end_data, &start_brk, &env_start, &env_end); | |
a6f151a7 CB |
280 | if (i != 5) |
281 | return -1; | |
282 | ||
283 | /* Include the null byte here, because in the calculations below we | |
284 | * want to have room for it. */ | |
285 | len = strlen(title) + 1; | |
286 | ||
e1d43053 RF |
287 | tmp_proctitle = realloc(proctitle, len); |
288 | if (!tmp_proctitle) | |
a6f151a7 CB |
289 | return -1; |
290 | ||
e1d43053 RF |
291 | proctitle = tmp_proctitle; |
292 | ||
f1170ed2 | 293 | arg_start = (unsigned long)proctitle; |
a6f151a7 CB |
294 | arg_end = arg_start + len; |
295 | ||
296 | brk_val = syscall(__NR_brk, 0); | |
297 | ||
f1170ed2 CB |
298 | prctl_map = (struct prctl_mm_map){ |
299 | .start_code = start_code, | |
300 | .end_code = end_code, | |
301 | .start_stack = start_stack, | |
302 | .start_data = start_data, | |
303 | .end_data = end_data, | |
304 | .start_brk = start_brk, | |
305 | .brk = brk_val, | |
306 | .arg_start = arg_start, | |
307 | .arg_end = arg_end, | |
308 | .env_start = env_start, | |
309 | .env_end = env_end, | |
310 | .auxv = NULL, | |
311 | .auxv_size = 0, | |
312 | .exe_fd = -1, | |
a6f151a7 CB |
313 | }; |
314 | ||
b81689a1 CB |
315 | ret = prctl(PR_SET_MM, prctl_arg(PR_SET_MM_MAP), prctl_arg(&prctl_map), |
316 | prctl_arg(sizeof(prctl_map)), prctl_arg(0)); | |
a6f151a7 | 317 | if (ret == 0) |
f1170ed2 | 318 | (void)strlcpy((char *)arg_start, title, len); |
a6f151a7 | 319 | else |
7be6bcd5 | 320 | SYSWARN("Failed to set cmdline"); |
a6f151a7 CB |
321 | |
322 | return ret; | |
323 | } |