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