]>
Commit | Line | Data |
---|---|---|
9267beba CB |
1 | /* liblxcapi |
2 | * | |
3 | * Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>. | |
4 | * Copyright © 2018 Canonical Ltd. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | */ | |
19 | ||
8d313ae1 | 20 | #define _GNU_SOURCE |
9267beba | 21 | #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */ |
8d313ae1 | 22 | #include <ctype.h> |
4b7c0ef8 | 23 | #include <dirent.h> |
9267beba | 24 | #include <errno.h> |
23500ef5 | 25 | #include <fcntl.h> |
9267beba | 26 | #include <limits.h> |
23500ef5 | 27 | #include <sched.h> |
9267beba CB |
28 | #include <stdio.h> |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <strings.h> | |
32 | #include <unistd.h> | |
33 | #include <linux/sched.h> | |
d567a9a7 | 34 | #include <sys/mount.h> |
9267beba | 35 | #include <sys/prctl.h> |
0cb3e3a6 | 36 | #include <sys/stat.h> |
9267beba CB |
37 | #include <sys/types.h> |
38 | #include <sys/wait.h> | |
39 | ||
2e607d1c KY |
40 | #include "config.h" |
41 | ||
9267beba CB |
42 | #if HAVE_SYS_PERSONALITY_H |
43 | #include <sys/personality.h> | |
44 | #endif | |
45 | ||
46 | #include <lxc/lxccontainer.h> | |
47 | ||
d567a9a7 | 48 | #include "arguments.h" |
9267beba CB |
49 | #include "tool_utils.h" |
50 | ||
43f984ea DJ |
51 | #ifndef HAVE_STRLCPY |
52 | #include "include/strlcpy.h" | |
53 | #endif | |
54 | ||
9267beba CB |
55 | int lxc_fill_elevated_privileges(char *flaglist, int *flags) |
56 | { | |
57 | char *token, *saveptr = NULL; | |
58 | int i, aflag; | |
59 | struct { | |
60 | const char *token; | |
61 | int flag; | |
62 | } all_privs[] = { | |
63 | { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, | |
64 | { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, | |
65 | { "LSM", LXC_ATTACH_LSM_EXEC }, | |
66 | { NULL, 0 } | |
67 | }; | |
68 | ||
69 | if (!flaglist) { | |
70 | /* For the sake of backward compatibility, drop all privileges | |
71 | * if none is specified. | |
72 | */ | |
73 | for (i = 0; all_privs[i].token; i++) | |
74 | *flags |= all_privs[i].flag; | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | token = strtok_r(flaglist, "|", &saveptr); | |
80 | while (token) { | |
81 | aflag = -1; | |
82 | for (i = 0; all_privs[i].token; i++) | |
83 | if (!strcmp(all_privs[i].token, token)) | |
84 | aflag = all_privs[i].flag; | |
85 | if (aflag < 0) | |
86 | return -1; | |
87 | ||
88 | *flags |= aflag; | |
89 | ||
90 | token = strtok_r(NULL, "|", &saveptr); | |
91 | } | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | signed long lxc_config_parse_arch(const char *arch) | |
97 | { | |
98 | #if HAVE_SYS_PERSONALITY_H | |
99 | size_t i; | |
100 | struct per_name { | |
101 | char *name; | |
102 | unsigned long per; | |
103 | } pername[] = { | |
104 | { "x86", PER_LINUX32 }, | |
105 | { "linux32", PER_LINUX32 }, | |
106 | { "i386", PER_LINUX32 }, | |
107 | { "i486", PER_LINUX32 }, | |
108 | { "i586", PER_LINUX32 }, | |
109 | { "i686", PER_LINUX32 }, | |
110 | { "athlon", PER_LINUX32 }, | |
111 | { "mips", PER_LINUX32 }, | |
112 | { "mipsel", PER_LINUX32 }, | |
113 | { "ppc", PER_LINUX32 }, | |
114 | { "arm", PER_LINUX32 }, | |
115 | { "armv7l", PER_LINUX32 }, | |
116 | { "armhf", PER_LINUX32 }, | |
117 | { "armel", PER_LINUX32 }, | |
118 | { "powerpc", PER_LINUX32 }, | |
119 | { "linux64", PER_LINUX }, | |
120 | { "x86_64", PER_LINUX }, | |
121 | { "amd64", PER_LINUX }, | |
122 | { "mips64", PER_LINUX }, | |
123 | { "mips64el", PER_LINUX }, | |
124 | { "ppc64", PER_LINUX }, | |
125 | { "ppc64le", PER_LINUX }, | |
126 | { "ppc64el", PER_LINUX }, | |
127 | { "powerpc64", PER_LINUX }, | |
128 | { "s390x", PER_LINUX }, | |
129 | { "aarch64", PER_LINUX }, | |
130 | { "arm64", PER_LINUX }, | |
131 | }; | |
132 | size_t len = sizeof(pername) / sizeof(pername[0]); | |
133 | ||
134 | for (i = 0; i < len; i++) { | |
135 | if (!strcmp(pername[i].name, arch)) | |
136 | return pername[i].per; | |
137 | } | |
138 | #endif | |
139 | ||
140 | return -1; | |
141 | } | |
142 | ||
9267beba CB |
143 | const static struct ns_info { |
144 | const char *proc_name; | |
145 | int clone_flag; | |
146 | } ns_info[LXC_NS_MAX] = { | |
147 | [LXC_NS_USER] = { "user", CLONE_NEWUSER }, | |
148 | [LXC_NS_MNT] = { "mnt", CLONE_NEWNS }, | |
149 | [LXC_NS_PID] = { "pid", CLONE_NEWPID }, | |
150 | [LXC_NS_UTS] = { "uts", CLONE_NEWUTS }, | |
151 | [LXC_NS_IPC] = { "ipc", CLONE_NEWIPC }, | |
152 | [LXC_NS_NET] = { "net", CLONE_NEWNET }, | |
153 | [LXC_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP } | |
154 | }; | |
155 | ||
156 | int lxc_namespace_2_cloneflag(const char *namespace) | |
157 | { | |
158 | int i; | |
159 | for (i = 0; i < LXC_NS_MAX; i++) | |
160 | if (!strcasecmp(ns_info[i].proc_name, namespace)) | |
161 | return ns_info[i].clone_flag; | |
162 | ||
163 | fprintf(stderr, "Invalid namespace name \"%s\"", namespace); | |
164 | return -EINVAL; | |
165 | } | |
166 | ||
167 | int lxc_fill_namespace_flags(char *flaglist, int *flags) | |
168 | { | |
169 | char *token, *saveptr = NULL; | |
170 | int aflag; | |
171 | ||
172 | if (!flaglist) { | |
173 | fprintf(stderr, "At least one namespace is needed\n"); | |
174 | return -1; | |
175 | } | |
176 | ||
177 | token = strtok_r(flaglist, "|", &saveptr); | |
178 | while (token) { | |
179 | ||
180 | aflag = lxc_namespace_2_cloneflag(token); | |
181 | if (aflag < 0) | |
182 | return -1; | |
183 | ||
184 | *flags |= aflag; | |
185 | ||
186 | token = strtok_r(NULL, "|", &saveptr); | |
187 | } | |
188 | ||
189 | return 0; | |
190 | } | |
191 | ||
192 | #if HAVE_LIBCAP | |
193 | ||
194 | #ifndef PR_CAPBSET_READ | |
195 | #define PR_CAPBSET_READ 23 | |
196 | #endif | |
197 | ||
198 | int lxc_caps_init(void) | |
199 | { | |
200 | uid_t uid = getuid(); | |
201 | gid_t gid = getgid(); | |
202 | uid_t euid = geteuid(); | |
203 | ||
204 | if (!uid) | |
205 | return 0; | |
206 | ||
207 | if (uid && !euid) { | |
208 | if (prctl(PR_SET_KEEPCAPS, 1)) { | |
209 | fprintf(stderr, "%s - Failed to set PR_SET_KEEPCAPS\n", strerror(errno)); | |
210 | return -1; | |
211 | } | |
212 | ||
213 | if (setresgid(gid, gid, gid)) { | |
214 | fprintf(stderr, "%s - Failed to change gid to %d\n", strerror(errno), gid); | |
215 | return -1; | |
216 | } | |
217 | ||
218 | if (setresuid(uid, uid, uid)) { | |
219 | fprintf(stderr, "%s - Failed to change uid to %d\n", strerror(errno), uid); | |
220 | return -1; | |
221 | } | |
222 | ||
223 | if (lxc_caps_up()) { | |
224 | fprintf(stderr, "%s - Failed to restore capabilities\n", strerror(errno)); | |
225 | return -1; | |
226 | } | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | int lxc_caps_up(void) | |
233 | { | |
234 | cap_t caps; | |
235 | cap_value_t cap; | |
236 | int ret; | |
237 | ||
238 | /* when we are run as root, we don't want to play | |
239 | * with the capabilities */ | |
240 | if (!getuid()) | |
241 | return 0; | |
242 | ||
243 | caps = cap_get_proc(); | |
244 | if (!caps) { | |
245 | fprintf(stderr, "%s - Failed to cap_get_proc\n", strerror(errno)); | |
246 | return -1; | |
247 | } | |
248 | ||
249 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
250 | cap_flag_value_t flag; | |
251 | ||
252 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
253 | if (ret) { | |
254 | if (errno == EINVAL) { | |
255 | break; | |
256 | } else { | |
257 | fprintf(stderr, "%s- Failed to call cap_get_flag\n", strerror(errno)); | |
258 | goto out; | |
259 | } | |
260 | } | |
261 | ||
262 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
263 | if (ret) { | |
264 | fprintf(stderr, "%s - Failed to call cap_set_flag", strerror(errno)); | |
265 | goto out; | |
266 | } | |
267 | } | |
268 | ||
269 | ret = cap_set_proc(caps); | |
270 | if (ret) { | |
271 | fprintf(stderr, "%s - Failed to cap_set_proc", strerror(errno)); | |
272 | goto out; | |
273 | } | |
274 | ||
275 | out: | |
276 | cap_free(caps); | |
277 | return 0; | |
278 | } | |
279 | ||
280 | #endif | |
281 | ||
78485176 CB |
282 | int wait_for_pid(pid_t pid) |
283 | { | |
284 | int status, ret; | |
285 | ||
286 | again: | |
287 | ret = waitpid(pid, &status, 0); | |
288 | if (ret == -1) { | |
289 | if (errno == EINTR) | |
290 | goto again; | |
291 | return -1; | |
292 | } | |
293 | if (ret != pid) | |
294 | goto again; | |
295 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
296 | return -1; | |
297 | return 0; | |
298 | } | |
299 | ||
9267beba CB |
300 | int lxc_wait_for_pid_status(pid_t pid) |
301 | { | |
302 | int status, ret; | |
303 | ||
304 | again: | |
305 | ret = waitpid(pid, &status, 0); | |
306 | if (ret == -1) { | |
307 | if (errno == EINTR) | |
308 | goto again; | |
309 | return -1; | |
310 | } | |
311 | if (ret != pid) | |
312 | goto again; | |
313 | return status; | |
314 | } | |
315 | ||
8d313ae1 CB |
316 | int lxc_safe_uint(const char *numstr, unsigned int *converted) |
317 | { | |
318 | char *err = NULL; | |
319 | unsigned long int uli; | |
320 | ||
321 | while (isspace(*numstr)) | |
322 | numstr++; | |
323 | ||
324 | if (*numstr == '-') | |
325 | return -EINVAL; | |
326 | ||
327 | errno = 0; | |
328 | uli = strtoul(numstr, &err, 0); | |
329 | if (errno == ERANGE && uli == ULONG_MAX) | |
330 | return -ERANGE; | |
331 | ||
332 | if (err == numstr || *err != '\0') | |
333 | return -EINVAL; | |
334 | ||
335 | if (uli > UINT_MAX) | |
336 | return -ERANGE; | |
337 | ||
338 | *converted = (unsigned int)uli; | |
339 | return 0; | |
340 | } | |
341 | ||
9267beba CB |
342 | int lxc_safe_int(const char *numstr, int *converted) |
343 | { | |
344 | char *err = NULL; | |
345 | signed long int sli; | |
346 | ||
347 | errno = 0; | |
348 | sli = strtol(numstr, &err, 0); | |
349 | if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) | |
350 | return -ERANGE; | |
351 | ||
352 | if (errno != 0 && sli == 0) | |
353 | return -EINVAL; | |
354 | ||
355 | if (err == numstr || *err != '\0') | |
356 | return -EINVAL; | |
357 | ||
358 | if (sli > INT_MAX || sli < INT_MIN) | |
359 | return -ERANGE; | |
360 | ||
361 | *converted = (int)sli; | |
362 | return 0; | |
363 | } | |
364 | ||
365 | int lxc_safe_long(const char *numstr, long int *converted) | |
366 | { | |
367 | char *err = NULL; | |
368 | signed long int sli; | |
369 | ||
370 | errno = 0; | |
371 | sli = strtol(numstr, &err, 0); | |
372 | if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN)) | |
373 | return -ERANGE; | |
374 | ||
375 | if (errno != 0 && sli == 0) | |
376 | return -EINVAL; | |
377 | ||
378 | if (err == numstr || *err != '\0') | |
379 | return -EINVAL; | |
380 | ||
381 | *converted = sli; | |
382 | return 0; | |
383 | } | |
0cb3e3a6 CB |
384 | |
385 | void lxc_free_array(void **array, lxc_free_fn element_free_fn) | |
386 | { | |
387 | void **p; | |
388 | for (p = array; p && *p; p++) | |
389 | element_free_fn(*p); | |
390 | free((void*)array); | |
391 | } | |
392 | ||
393 | int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment) | |
394 | { | |
395 | size_t new_capacity; | |
396 | void **new_array; | |
397 | ||
398 | /* first time around, catch some trivial mistakes of the user | |
399 | * only initializing one of these */ | |
400 | if (!*array || !*capacity) { | |
401 | *array = NULL; | |
402 | *capacity = 0; | |
403 | } | |
404 | ||
405 | new_capacity = *capacity; | |
406 | while (new_size + 1 > new_capacity) | |
407 | new_capacity += capacity_increment; | |
408 | if (new_capacity != *capacity) { | |
409 | /* we have to reallocate */ | |
410 | new_array = realloc(*array, new_capacity * sizeof(void *)); | |
411 | if (!new_array) | |
412 | return -1; | |
413 | memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *)); | |
414 | *array = new_array; | |
415 | *capacity = new_capacity; | |
416 | } | |
417 | ||
418 | /* array has sufficient elements */ | |
419 | return 0; | |
420 | } | |
421 | ||
422 | char **lxc_string_split(const char *string, char _sep) | |
423 | { | |
424 | char *token, *str, *saveptr = NULL; | |
425 | char sep[2] = {_sep, '\0'}; | |
426 | char **tmp = NULL, **result = NULL; | |
427 | size_t result_capacity = 0; | |
428 | size_t result_count = 0; | |
43f984ea | 429 | size_t len; |
0cb3e3a6 CB |
430 | int r, saved_errno; |
431 | ||
432 | if (!string) | |
433 | return calloc(1, sizeof(char *)); | |
434 | ||
43f984ea DJ |
435 | len = strlen(string); |
436 | str = alloca(len + 1); | |
437 | (void)strlcpy(str, string, len + 1); | |
438 | ||
0cb3e3a6 CB |
439 | for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) { |
440 | r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); | |
441 | if (r < 0) | |
442 | goto error_out; | |
443 | result[result_count] = strdup(token); | |
444 | if (!result[result_count]) | |
445 | goto error_out; | |
446 | result_count++; | |
447 | } | |
448 | ||
449 | /* if we allocated too much, reduce it */ | |
450 | tmp = realloc(result, (result_count + 1) * sizeof(char *)); | |
451 | if (!tmp) | |
452 | goto error_out; | |
453 | result = tmp; | |
454 | /* Make sure we don't return uninitialized memory. */ | |
455 | if (result_count == 0) | |
456 | *result = NULL; | |
457 | return result; | |
458 | error_out: | |
459 | saved_errno = errno; | |
460 | lxc_free_array((void **)result, free); | |
461 | errno = saved_errno; | |
462 | return NULL; | |
463 | } | |
464 | ||
465 | char **lxc_normalize_path(const char *path) | |
466 | { | |
467 | char **components; | |
468 | char **p; | |
469 | size_t components_len = 0; | |
470 | size_t pos = 0; | |
471 | ||
472 | components = lxc_string_split(path, '/'); | |
473 | if (!components) | |
474 | return NULL; | |
475 | for (p = components; *p; p++) | |
476 | components_len++; | |
477 | ||
478 | /* resolve '.' and '..' */ | |
479 | for (pos = 0; pos < components_len; ) { | |
480 | if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) { | |
481 | /* eat this element */ | |
482 | free(components[pos]); | |
483 | memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos)); | |
484 | components_len--; | |
485 | } else if (!strcmp(components[pos], "..")) { | |
486 | /* eat this and the previous element */ | |
487 | free(components[pos - 1]); | |
488 | free(components[pos]); | |
489 | memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos)); | |
490 | components_len -= 2; | |
491 | pos--; | |
492 | } else { | |
493 | pos++; | |
494 | } | |
495 | } | |
496 | ||
497 | return components; | |
498 | } | |
499 | ||
500 | char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix) | |
501 | { | |
502 | char *result; | |
503 | char **p; | |
504 | size_t sep_len = strlen(sep); | |
505 | size_t result_len = use_as_prefix * sep_len; | |
506 | ||
507 | /* calculate new string length */ | |
508 | for (p = (char **)parts; *p; p++) | |
509 | result_len += (p > (char **)parts) * sep_len + strlen(*p); | |
510 | ||
511 | result = calloc(result_len + 1, 1); | |
512 | if (!result) | |
513 | return NULL; | |
514 | ||
515 | if (use_as_prefix) | |
43f984ea DJ |
516 | (void)strlcpy(result, sep, result_len + 1); |
517 | ||
0cb3e3a6 CB |
518 | for (p = (char **)parts; *p; p++) { |
519 | if (p > (char **)parts) | |
520 | strcat(result, sep); | |
521 | strcat(result, *p); | |
522 | } | |
523 | ||
524 | return result; | |
525 | } | |
526 | ||
527 | int is_dir(const char *path) | |
528 | { | |
529 | struct stat statbuf; | |
530 | int ret = stat(path, &statbuf); | |
531 | if (ret == 0 && S_ISDIR(statbuf.st_mode)) | |
532 | return 1; | |
533 | return 0; | |
534 | } | |
535 | ||
536 | size_t lxc_array_len(void **array) | |
537 | { | |
538 | void **p; | |
539 | size_t result = 0; | |
540 | ||
541 | for (p = array; p && *p; p++) | |
542 | result++; | |
543 | ||
544 | return result; | |
545 | } | |
e6294545 CB |
546 | |
547 | /* | |
548 | * Given the '-t' template option to lxc-create, figure out what to | |
549 | * do. If the template is a full executable path, use that. If it | |
550 | * is something like 'sshd', then return $templatepath/lxc-sshd. | |
551 | * On success return the template, on error return NULL. | |
552 | */ | |
553 | char *get_template_path(const char *t) | |
554 | { | |
555 | int ret, len; | |
556 | char *tpath; | |
557 | ||
558 | if (t[0] == '/' && access(t, X_OK) == 0) { | |
559 | tpath = strdup(t); | |
560 | return tpath; | |
561 | } | |
562 | ||
563 | len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; | |
564 | tpath = malloc(len); | |
565 | if (!tpath) | |
566 | return NULL; | |
567 | ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); | |
568 | if (ret < 0 || ret >= len) { | |
569 | free(tpath); | |
570 | return NULL; | |
571 | } | |
572 | if (access(tpath, X_OK) < 0) { | |
573 | fprintf(stderr, "Bad template: %s\n", t); | |
574 | free(tpath); | |
575 | return NULL; | |
576 | } | |
577 | ||
578 | return tpath; | |
579 | } | |
580 | ||
581 | int mkdir_p(const char *dir, mode_t mode) | |
582 | { | |
583 | const char *tmp = dir; | |
584 | const char *orig = dir; | |
585 | char *makeme; | |
586 | ||
587 | do { | |
588 | dir = tmp + strspn(tmp, "/"); | |
589 | tmp = dir + strcspn(dir, "/"); | |
590 | makeme = strndup(orig, dir - orig); | |
591 | if (*makeme) { | |
592 | if (mkdir(makeme, mode) && errno != EEXIST) { | |
593 | fprintf(stderr, "Failed to create directory \"%s\"\n", makeme); | |
594 | free(makeme); | |
595 | return -1; | |
596 | } | |
597 | } | |
598 | free(makeme); | |
599 | } while(tmp != dir); | |
600 | ||
601 | return 0; | |
602 | } | |
e3347eef CB |
603 | |
604 | bool file_exists(const char *f) | |
605 | { | |
606 | struct stat statbuf; | |
607 | ||
608 | return stat(f, &statbuf) == 0; | |
609 | } | |
23500ef5 CB |
610 | |
611 | bool switch_to_ns(pid_t pid, const char *ns) { | |
612 | int fd, ret; | |
613 | char nspath[TOOL_MAXPATHLEN]; | |
614 | ||
615 | /* Switch to new ns */ | |
616 | ret = snprintf(nspath, TOOL_MAXPATHLEN, "/proc/%d/ns/%s", pid, ns); | |
617 | if (ret < 0 || ret >= TOOL_MAXPATHLEN) | |
618 | return false; | |
619 | ||
620 | fd = open(nspath, O_RDONLY); | |
621 | if (fd < 0) { | |
622 | fprintf(stderr, "Failed to open %s\n", nspath); | |
623 | return false; | |
624 | } | |
625 | ||
626 | ret = setns(fd, 0); | |
627 | if (ret) { | |
628 | fprintf(stderr, "Failed to set process %d to %s of %d\n", pid, ns, fd); | |
629 | close(fd); | |
630 | return false; | |
631 | } | |
632 | close(fd); | |
633 | return true; | |
634 | } | |
791e7a73 CB |
635 | |
636 | static bool complete_word(char ***result, char *start, char *end, size_t *cap, size_t *cnt) | |
637 | { | |
638 | int r; | |
639 | ||
640 | r = lxc_grow_array((void ***)result, cap, 2 + *cnt, 16); | |
641 | if (r < 0) | |
642 | return false; | |
643 | (*result)[*cnt] = strndup(start, end - start); | |
644 | if (!(*result)[*cnt]) | |
645 | return false; | |
646 | (*cnt)++; | |
647 | ||
648 | return true; | |
649 | } | |
650 | ||
651 | /* | |
652 | * Given a a string 'one two "three four"', split into three words, | |
653 | * one, two, and "three four" | |
654 | */ | |
655 | char **lxc_string_split_quoted(char *string) | |
656 | { | |
657 | char *nextword = string, *p, state; | |
658 | char **result = NULL; | |
659 | size_t result_capacity = 0; | |
660 | size_t result_count = 0; | |
661 | ||
662 | if (!string || !*string) | |
663 | return calloc(1, sizeof(char *)); | |
664 | ||
665 | // TODO I'm *not* handling escaped quote | |
666 | state = ' '; | |
667 | for (p = string; *p; p++) { | |
668 | switch(state) { | |
669 | case ' ': | |
670 | if (isspace(*p)) | |
671 | continue; | |
672 | else if (*p == '"' || *p == '\'') { | |
673 | nextword = p; | |
674 | state = *p; | |
675 | continue; | |
676 | } | |
677 | nextword = p; | |
678 | state = 'a'; | |
679 | continue; | |
680 | case 'a': | |
681 | if (isspace(*p)) { | |
682 | complete_word(&result, nextword, p, &result_capacity, &result_count); | |
683 | state = ' '; | |
684 | continue; | |
685 | } | |
686 | continue; | |
687 | case '"': | |
688 | case '\'': | |
689 | if (*p == state) { | |
690 | complete_word(&result, nextword+1, p, &result_capacity, &result_count); | |
691 | state = ' '; | |
692 | continue; | |
693 | } | |
694 | continue; | |
695 | } | |
696 | } | |
697 | ||
698 | if (state == 'a') | |
699 | complete_word(&result, nextword, p, &result_capacity, &result_count); | |
700 | ||
701 | return realloc(result, (result_count + 1) * sizeof(char *)); | |
702 | } | |
703 | ||
704 | int lxc_char_left_gc(const char *buffer, size_t len) | |
705 | { | |
706 | size_t i; | |
707 | for (i = 0; i < len; i++) { | |
708 | if (buffer[i] == ' ' || | |
709 | buffer[i] == '\t') | |
710 | continue; | |
711 | return i; | |
712 | } | |
713 | return 0; | |
714 | } | |
715 | ||
716 | int lxc_char_right_gc(const char *buffer, size_t len) | |
717 | { | |
718 | int i; | |
719 | for (i = len - 1; i >= 0; i--) { | |
720 | if (buffer[i] == ' ' || | |
721 | buffer[i] == '\t' || | |
722 | buffer[i] == '\n' || | |
723 | buffer[i] == '\0') | |
724 | continue; | |
725 | return i + 1; | |
726 | } | |
727 | return 0; | |
728 | } | |
729 | ||
730 | struct new_config_item *parse_line(char *buffer) | |
731 | { | |
732 | char *dot, *key, *line, *linep, *value; | |
733 | int ret = 0; | |
734 | char *dup = buffer; | |
735 | struct new_config_item *new = NULL; | |
736 | ||
737 | linep = line = strdup(dup); | |
738 | if (!line) | |
739 | return NULL; | |
740 | ||
741 | line += lxc_char_left_gc(line, strlen(line)); | |
742 | ||
743 | /* martian option - don't add it to the config itself */ | |
744 | if (strncmp(line, "lxc.", 4)) | |
745 | goto on_error; | |
746 | ||
747 | ret = -1; | |
748 | dot = strchr(line, '='); | |
749 | if (!dot) { | |
750 | fprintf(stderr, "Invalid configuration item: %s\n", line); | |
751 | goto on_error; | |
752 | } | |
753 | ||
754 | *dot = '\0'; | |
755 | value = dot + 1; | |
756 | ||
757 | key = line; | |
758 | key[lxc_char_right_gc(key, strlen(key))] = '\0'; | |
759 | ||
760 | value += lxc_char_left_gc(value, strlen(value)); | |
761 | value[lxc_char_right_gc(value, strlen(value))] = '\0'; | |
762 | ||
763 | if (*value == '\'' || *value == '\"') { | |
764 | size_t len; | |
765 | ||
766 | len = strlen(value); | |
767 | if (len > 1 && value[len - 1] == *value) { | |
768 | value[len - 1] = '\0'; | |
769 | value++; | |
770 | } | |
771 | } | |
772 | ||
773 | ret = -1; | |
774 | new = malloc(sizeof(struct new_config_item)); | |
775 | if (!new) | |
776 | goto on_error; | |
777 | ||
778 | new->key = strdup(key); | |
779 | new->val = strdup(value); | |
780 | if (!new->val || !new->key) | |
781 | goto on_error; | |
782 | ret = 0; | |
783 | ||
784 | on_error: | |
785 | free(linep); | |
786 | if (ret < 0 && new) { | |
787 | free(new->key); | |
788 | free(new->val); | |
789 | free(new); | |
790 | new = NULL; | |
791 | } | |
792 | ||
793 | return new; | |
794 | } | |
795 | ||
796 | int lxc_config_define_add(struct lxc_list *defines, char *arg) | |
797 | { | |
798 | struct lxc_list *dent; | |
799 | ||
800 | dent = malloc(sizeof(struct lxc_list)); | |
801 | if (!dent) | |
802 | return -1; | |
803 | ||
804 | dent->elem = parse_line(arg); | |
74269047 C |
805 | if (!dent->elem) { |
806 | free(dent); | |
791e7a73 | 807 | return -1; |
74269047 C |
808 | } |
809 | ||
791e7a73 CB |
810 | lxc_list_add_tail(defines, dent); |
811 | return 0; | |
812 | } | |
813 | ||
e2eae703 | 814 | bool lxc_config_define_load(struct lxc_list *defines, struct lxc_container *c) |
791e7a73 CB |
815 | { |
816 | struct lxc_list *it; | |
e2eae703 | 817 | bool bret = true; |
791e7a73 CB |
818 | |
819 | lxc_list_for_each(it, defines) { | |
820 | struct new_config_item *new_item = it->elem; | |
e2eae703 FA |
821 | bret = c->set_config_item(c, new_item->key, new_item->val); |
822 | if (!bret) | |
791e7a73 CB |
823 | break; |
824 | } | |
825 | ||
826 | lxc_config_define_free(defines); | |
e2eae703 | 827 | return bret; |
791e7a73 CB |
828 | } |
829 | ||
830 | void lxc_config_define_free(struct lxc_list *defines) | |
831 | { | |
832 | struct lxc_list *it, *next; | |
833 | ||
834 | lxc_list_for_each_safe(it, defines, next) { | |
835 | struct new_config_item *new_item = it->elem; | |
836 | free(new_item->key); | |
837 | free(new_item->val); | |
838 | lxc_list_del(it); | |
839 | free(it); | |
840 | } | |
841 | } | |
8765242a CB |
842 | |
843 | int lxc_read_from_file(const char *filename, void* buf, size_t count) | |
844 | { | |
845 | int fd = -1, saved_errno; | |
846 | ssize_t ret; | |
847 | ||
848 | fd = open(filename, O_RDONLY | O_CLOEXEC); | |
849 | if (fd < 0) | |
850 | return -1; | |
851 | ||
852 | if (!buf || !count) { | |
853 | char buf2[100]; | |
854 | size_t count2 = 0; | |
855 | while ((ret = read(fd, buf2, 100)) > 0) | |
856 | count2 += ret; | |
857 | if (ret >= 0) | |
858 | ret = count2; | |
859 | } else { | |
860 | memset(buf, 0, count); | |
861 | ret = read(fd, buf, count); | |
862 | } | |
863 | ||
864 | saved_errno = errno; | |
865 | close(fd); | |
866 | errno = saved_errno; | |
867 | return ret; | |
868 | } | |
4b7c0ef8 CB |
869 | |
870 | char **lxc_string_split_and_trim(const char *string, char _sep) | |
871 | { | |
872 | char *token, *str, *saveptr = NULL; | |
873 | char sep[2] = { _sep, '\0' }; | |
874 | char **result = NULL; | |
875 | size_t result_capacity = 0; | |
876 | size_t result_count = 0; | |
877 | int r, saved_errno; | |
878 | size_t i = 0; | |
43f984ea | 879 | size_t len; |
4b7c0ef8 CB |
880 | |
881 | if (!string) | |
882 | return calloc(1, sizeof(char *)); | |
883 | ||
43f984ea DJ |
884 | len = strlen(string); |
885 | str = alloca(len + 1); | |
886 | (void)strlcpy(str, string, len + 1); | |
887 | ||
4b7c0ef8 CB |
888 | for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) { |
889 | while (token[0] == ' ' || token[0] == '\t') | |
890 | token++; | |
891 | i = strlen(token); | |
892 | while (i > 0 && (token[i - 1] == ' ' || token[i - 1] == '\t')) { | |
893 | token[i - 1] = '\0'; | |
894 | i--; | |
895 | } | |
896 | r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16); | |
897 | if (r < 0) | |
898 | goto error_out; | |
899 | result[result_count] = strdup(token); | |
900 | if (!result[result_count]) | |
901 | goto error_out; | |
902 | result_count++; | |
903 | } | |
904 | ||
905 | /* if we allocated too much, reduce it */ | |
906 | return realloc(result, (result_count + 1) * sizeof(char *)); | |
907 | error_out: | |
908 | saved_errno = errno; | |
909 | lxc_free_array((void **)result, free); | |
910 | errno = saved_errno; | |
911 | return NULL; | |
912 | } | |
913 | ||
914 | char *lxc_append_paths(const char *first, const char *second) | |
915 | { | |
916 | int ret; | |
917 | size_t len; | |
918 | char *result = NULL; | |
919 | const char *pattern = "%s%s"; | |
920 | ||
921 | len = strlen(first) + strlen(second) + 1; | |
922 | if (second[0] != '/') { | |
923 | len += 1; | |
924 | pattern = "%s/%s"; | |
925 | } | |
926 | ||
927 | result = calloc(1, len); | |
928 | if (!result) | |
929 | return NULL; | |
930 | ||
931 | ret = snprintf(result, len, pattern, first, second); | |
932 | if (ret < 0 || (size_t)ret >= len) { | |
933 | free(result); | |
934 | return NULL; | |
935 | } | |
936 | ||
937 | return result; | |
938 | } | |
939 | ||
940 | bool dir_exists(const char *path) | |
941 | { | |
942 | struct stat sb; | |
943 | int ret; | |
944 | ||
945 | ret = stat(path, &sb); | |
946 | if (ret < 0) | |
947 | /* Could be something other than eexist, just say "no". */ | |
948 | return false; | |
949 | return S_ISDIR(sb.st_mode); | |
950 | } | |
951 | ||
952 | char *lxc_string_replace(const char *needle, const char *replacement, | |
953 | const char *haystack) | |
954 | { | |
955 | ssize_t len = -1, saved_len = -1; | |
956 | char *result = NULL; | |
957 | size_t replacement_len = strlen(replacement); | |
958 | size_t needle_len = strlen(needle); | |
959 | ||
960 | /* should be executed exactly twice */ | |
961 | while (len == -1 || result == NULL) { | |
962 | char *p; | |
963 | char *last_p; | |
964 | ssize_t part_len; | |
965 | ||
966 | if (len != -1) { | |
967 | result = calloc(1, len + 1); | |
968 | if (!result) | |
969 | return NULL; | |
970 | saved_len = len; | |
971 | } | |
972 | ||
973 | len = 0; | |
974 | ||
975 | for (last_p = (char *)haystack, p = strstr(last_p, needle); p; last_p = p, p = strstr(last_p, needle)) { | |
976 | part_len = (ssize_t)(p - last_p); | |
977 | if (result && part_len > 0) | |
978 | memcpy(&result[len], last_p, part_len); | |
979 | len += part_len; | |
980 | if (result && replacement_len > 0) | |
981 | memcpy(&result[len], replacement, replacement_len); | |
982 | len += replacement_len; | |
983 | p += needle_len; | |
984 | } | |
985 | part_len = strlen(last_p); | |
986 | if (result && part_len > 0) | |
987 | memcpy(&result[len], last_p, part_len); | |
988 | len += part_len; | |
989 | } | |
990 | ||
991 | /* make sure we did the same thing twice, | |
992 | * once for calculating length, the other | |
993 | * time for copying data */ | |
994 | if (saved_len != len) { | |
995 | free(result); | |
996 | return NULL; | |
997 | } | |
998 | /* make sure we didn't overwrite any buffer, | |
999 | * due to calloc the string should be 0-terminated */ | |
1000 | if (result[len] != '\0') { | |
1001 | free(result); | |
1002 | return NULL; | |
1003 | } | |
1004 | ||
1005 | return result; | |
1006 | } | |
1007 | ||
1008 | ssize_t lxc_write_nointr(int fd, const void* buf, size_t count) | |
1009 | { | |
1010 | ssize_t ret; | |
1011 | again: | |
1012 | ret = write(fd, buf, count); | |
1013 | if (ret < 0 && errno == EINTR) | |
1014 | goto again; | |
1015 | return ret; | |
1016 | } | |
1017 | ||
1018 | char *get_rundir() | |
1019 | { | |
1020 | char *rundir; | |
1021 | const char *homedir; | |
1022 | ||
1023 | if (geteuid() == 0) { | |
1024 | rundir = strdup(RUNTIME_PATH); | |
1025 | return rundir; | |
1026 | } | |
1027 | ||
1028 | rundir = getenv("XDG_RUNTIME_DIR"); | |
1029 | if (rundir) { | |
1030 | rundir = strdup(rundir); | |
1031 | return rundir; | |
1032 | } | |
1033 | ||
1034 | homedir = getenv("HOME"); | |
1035 | if (!homedir) | |
1036 | return NULL; | |
1037 | ||
1038 | rundir = malloc(sizeof(char) * (17 + strlen(homedir))); | |
1039 | sprintf(rundir, "%s/.cache/lxc/run/", homedir); | |
1040 | ||
1041 | return rundir; | |
1042 | } | |
1043 | ||
1044 | char *must_copy_string(const char *entry) | |
1045 | { | |
1046 | char *ret; | |
1047 | ||
1048 | if (!entry) | |
1049 | return NULL; | |
1050 | do { | |
1051 | ret = strdup(entry); | |
1052 | } while (!ret); | |
1053 | ||
1054 | return ret; | |
1055 | } | |
1056 | ||
1057 | ||
1058 | void *must_realloc(void *orig, size_t sz) | |
1059 | { | |
1060 | void *ret; | |
1061 | ||
1062 | do { | |
1063 | ret = realloc(orig, sz); | |
1064 | } while (!ret); | |
1065 | ||
1066 | return ret; | |
1067 | } | |
1068 | ||
1069 | char *must_make_path(const char *first, ...) | |
1070 | { | |
1071 | va_list args; | |
1072 | char *cur, *dest; | |
1073 | size_t full_len = strlen(first); | |
1074 | ||
1075 | dest = must_copy_string(first); | |
1076 | ||
1077 | va_start(args, first); | |
1078 | while ((cur = va_arg(args, char *)) != NULL) { | |
1079 | full_len += strlen(cur); | |
1080 | if (cur[0] != '/') | |
1081 | full_len++; | |
1082 | dest = must_realloc(dest, full_len + 1); | |
1083 | if (cur[0] != '/') | |
1084 | strcat(dest, "/"); | |
1085 | strcat(dest, cur); | |
1086 | } | |
1087 | va_end(args); | |
1088 | ||
1089 | return dest; | |
1090 | } | |
1091 | ||
1092 | int rm_r(char *dirname) | |
1093 | { | |
1094 | int ret; | |
1095 | struct dirent *direntp; | |
1096 | DIR *dir; | |
1097 | int r = 0; | |
1098 | ||
1099 | dir = opendir(dirname); | |
1100 | if (!dir) | |
1101 | return -1; | |
1102 | ||
1103 | while ((direntp = readdir(dir))) { | |
1104 | char *pathname; | |
1105 | struct stat mystat; | |
1106 | ||
4b7c0ef8 CB |
1107 | if (!strcmp(direntp->d_name, ".") || |
1108 | !strcmp(direntp->d_name, "..")) | |
1109 | continue; | |
1110 | ||
1111 | pathname = must_make_path(dirname, direntp->d_name, NULL); | |
1112 | ||
1113 | ret = lstat(pathname, &mystat); | |
1114 | if (ret < 0) { | |
1115 | r = -1; | |
1116 | goto next; | |
1117 | } | |
1118 | ||
1119 | if (!S_ISDIR(mystat.st_mode)) | |
1120 | goto next; | |
1121 | ||
1122 | ret = rm_r(pathname); | |
1123 | if (ret < 0) | |
1124 | r = -1; | |
1125 | next: | |
1126 | free(pathname); | |
1127 | } | |
1128 | ||
1129 | ret = rmdir(dirname); | |
1130 | if (ret < 0) | |
1131 | r = -1; | |
1132 | ||
1133 | ret = closedir(dir); | |
1134 | if (ret < 0) | |
1135 | r = -1; | |
1136 | ||
1137 | return r; | |
1138 | } | |
1139 | ||
1140 | ssize_t lxc_read_nointr(int fd, void* buf, size_t count) | |
1141 | { | |
1142 | ssize_t ret; | |
1143 | again: | |
1144 | ret = read(fd, buf, count); | |
1145 | if (ret < 0 && errno == EINTR) | |
1146 | goto again; | |
1147 | return ret; | |
1148 | } | |
d567a9a7 CB |
1149 | |
1150 | static int mount_fs(const char *source, const char *target, const char *type) | |
1151 | { | |
1152 | /* the umount may fail */ | |
1153 | if (umount(target) < 0) | |
1154 | ||
1155 | if (mount(source, target, type, 0, NULL) < 0) | |
1156 | return -1; | |
1157 | ||
1158 | return 0; | |
1159 | } | |
1160 | ||
1161 | void lxc_setup_fs(void) | |
1162 | { | |
1163 | (void)mount_fs("proc", "/proc", "proc"); | |
1164 | ||
1165 | /* if /dev has been populated by us, /dev/shm does not exist */ | |
1166 | if (access("/dev/shm", F_OK)) | |
1167 | (void)mkdir("/dev/shm", 0777); | |
1168 | ||
1169 | /* if we can't mount /dev/shm, continue anyway */ | |
1170 | (void)mount_fs("shmfs", "/dev/shm", "tmpfs"); | |
1171 | ||
1172 | /* If we were able to mount /dev/shm, then /dev exists */ | |
1173 | /* Sure, but it's read-only per config :) */ | |
1174 | if (access("/dev/mqueue", F_OK)) | |
1175 | (void)mkdir("/dev/mqueue", 0666); | |
1176 | ||
1177 | /* continue even without posix message queue support */ | |
1178 | (void)mount_fs("mqueue", "/dev/mqueue", "mqueue"); | |
1179 | } | |
1180 | ||
1181 | struct clone_arg { | |
1182 | int (*fn)(void *); | |
1183 | void *arg; | |
1184 | }; | |
1185 | ||
1186 | static int do_clone(void *arg) | |
1187 | { | |
1188 | struct clone_arg *clone_arg = arg; | |
1189 | return clone_arg->fn(clone_arg->arg); | |
1190 | } | |
1191 | ||
1192 | pid_t lxc_clone(int (*fn)(void *), void *arg, int flags) | |
1193 | { | |
1194 | struct clone_arg clone_arg = { | |
1195 | .fn = fn, | |
1196 | .arg = arg, | |
1197 | }; | |
1198 | ||
1199 | size_t stack_size = lxc_getpagesize(); | |
1200 | void *stack = alloca(stack_size); | |
1201 | pid_t ret; | |
1202 | ||
1203 | #ifdef __ia64__ | |
1204 | ret = __clone2(do_clone, stack, stack_size, flags | SIGCHLD, &clone_arg); | |
1205 | #else | |
1206 | ret = clone(do_clone, stack + stack_size, flags | SIGCHLD, &clone_arg); | |
1207 | #endif | |
1208 | return ret; | |
1209 | } |