]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/initutils.c
cgroups: flatten hierarchy
[mirror_lxc.git] / src / lxc / initutils.c
CommitLineData
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 20lxc_log_define(initutils, lxc);
4295c5de 21
4295c5de
SH
22static 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
43const 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
205out:
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 */
220int 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}