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