]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/confile_utils.c
Merge pull request #2080 from tych0/add-idmap-parse-error-message
[mirror_lxc.git] / src / lxc / confile_utils.c
1 /* liblxcapi
2 *
3 * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2017 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
20 #include "config.h"
21
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <arpa/inet.h>
27
28 #include "conf.h"
29 #include "confile.h"
30 #include "confile_utils.h"
31 #include "error.h"
32 #include "list.h"
33 #include "log.h"
34 #include "lxccontainer.h"
35 #include "network.h"
36 #include "parse.h"
37 #include "utils.h"
38
39 lxc_log_define(lxc_confile_utils, lxc);
40
41 int parse_idmaps(const char *idmap, char *type, unsigned long *nsid,
42 unsigned long *hostid, unsigned long *range)
43 {
44 int ret = -1;
45 unsigned long tmp_hostid, tmp_nsid, tmp_range;
46 char tmp_type;
47 char *window, *slide;
48 char *dup = NULL;
49
50 /* Duplicate string. */
51 dup = strdup(idmap);
52 if (!dup)
53 goto on_error;
54
55 /* A prototypical idmap entry would be: "u 1000 1000000 65536" */
56
57 /* align */
58 slide = window = dup;
59 /* skip whitespace */
60 slide += strspn(slide, " \t\r");
61 if (slide != window && *slide == '\0')
62 goto on_error;
63
64 /* Validate type. */
65 if (*slide != 'u' && *slide != 'g') {
66 ERROR("invalid mapping type: %c", *slide);
67 goto on_error;
68 }
69
70 /* Assign type. */
71 tmp_type = *slide;
72
73 /* move beyond type */
74 slide++;
75 /* align */
76 window = slide;
77 /* Validate that only whitespace follows. */
78 slide += strspn(slide, " \t\r");
79 /* There must be whitespace. */
80 if (slide == window)
81 goto on_error;
82
83 /* Mark beginning of nsuid. */
84 window = slide;
85 /* Validate that non-whitespace follows. */
86 slide += strcspn(slide, " \t\r");
87 /* There must be non-whitespace. */
88 if (slide == window || *slide == '\0')
89 goto on_error;
90 /* Mark end of nsuid. */
91 *slide = '\0';
92
93 /* Parse nsuid. */
94 if (lxc_safe_ulong(window, &tmp_nsid) < 0) {
95 ERROR("couldn't parse nsuid: %s", window);
96 goto on_error;
97 }
98
99 /* Move beyond \0. */
100 slide++;
101 /* Validate that only whitespace follows. */
102 slide += strspn(slide, " \t\r");
103 /* If there was only one whitespace then we whiped it with our \0 above.
104 * So only ensure that we're not at the end of the string.
105 */
106 if (*slide == '\0')
107 goto on_error;
108
109 /* Mark beginning of hostid. */
110 window = slide;
111 /* Validate that non-whitespace follows. */
112 slide += strcspn(slide, " \t\r");
113 /* There must be non-whitespace. */
114 if (slide == window || *slide == '\0')
115 goto on_error;
116 /* Mark end of nsuid. */
117 *slide = '\0';
118
119 /* Parse hostid. */
120 if (lxc_safe_ulong(window, &tmp_hostid) < 0) {
121 ERROR("couldn't parse hostid: %s", window);
122 goto on_error;
123 }
124
125 /* Move beyond \0. */
126 slide++;
127 /* Validate that only whitespace follows. */
128 slide += strspn(slide, " \t\r");
129 /* If there was only one whitespace then we whiped it with our \0 above.
130 * So only ensure that we're not at the end of the string.
131 */
132 if (*slide == '\0')
133 goto on_error;
134
135 /* Mark beginning of range. */
136 window = slide;
137 /* Validate that non-whitespace follows. */
138 slide += strcspn(slide, " \t\r");
139 /* There must be non-whitespace. */
140 if (slide == window)
141 goto on_error;
142
143 /* The range is the last valid entry we expect. So make sure that there
144 * is not trailing garbage and if there is, error out.
145 */
146 if (*(slide + strspn(slide, " \t\r\n")) != '\0')
147 goto on_error;
148 /* Mark end of range. */
149 *slide = '\0';
150
151 /* Parse range. */
152 if (lxc_safe_ulong(window, &tmp_range) < 0) {
153 ERROR("couldn't parse range: %s", window);
154 goto on_error;
155 }
156
157 *type = tmp_type;
158 *nsid = tmp_nsid;
159 *hostid = tmp_hostid;
160 *range = tmp_range;
161
162 /* Yay, we survived. */
163 ret = 0;
164
165 on_error:
166 free(dup);
167
168 return ret;
169 }
170
171 bool lxc_config_value_empty(const char *value)
172 {
173 if (value && strlen(value) > 0)
174 return false;
175
176 return true;
177 }
178
179 struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail)
180 {
181 struct lxc_list *newlist;
182 struct lxc_netdev *netdev = NULL;
183
184 /* network does not exist */
185 netdev = malloc(sizeof(*netdev));
186 if (!netdev)
187 return NULL;
188
189 memset(netdev, 0, sizeof(*netdev));
190 lxc_list_init(&netdev->ipv4);
191 lxc_list_init(&netdev->ipv6);
192
193 /* give network a unique index */
194 netdev->idx = idx;
195
196 /* prepare new list */
197 newlist = malloc(sizeof(*newlist));
198 if (!newlist) {
199 free(netdev);
200 return NULL;
201 }
202
203 lxc_list_init(newlist);
204 newlist->elem = netdev;
205
206 if (tail)
207 lxc_list_add_tail(networks, newlist);
208 else
209 lxc_list_add(networks, newlist);
210 return netdev;
211 }
212
213 /* Takes care of finding the correct netdev struct in the networks list or
214 * allocates a new one if it couldn't be found.
215 */
216 struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf,
217 unsigned int idx, bool allocate)
218 {
219 struct lxc_netdev *netdev = NULL;
220 struct lxc_list *networks = &conf->network;
221 struct lxc_list *insert = networks;
222
223 /* lookup network */
224 if (!lxc_list_empty(networks)) {
225 lxc_list_for_each(insert, networks) {
226 netdev = insert->elem;
227 if (netdev->idx == idx)
228 return netdev;
229 else if (netdev->idx > idx)
230 break;
231 }
232 }
233
234 if (!allocate)
235 return NULL;
236
237 return lxc_network_add(insert, idx, true);
238 }
239
240 void lxc_log_configured_netdevs(const struct lxc_conf *conf)
241 {
242 struct lxc_netdev *netdev;
243 struct lxc_list *it = (struct lxc_list *)&conf->network;;
244
245 if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) &&
246 (lxc_log_get_level() != LXC_LOG_LEVEL_TRACE))
247 return;
248
249 if (lxc_list_empty(it)) {
250 TRACE("container has no networks configured");
251 return;
252 }
253
254 lxc_list_for_each(it, &conf->network) {
255 struct lxc_list *cur, *next;
256 struct lxc_inetdev *inet4dev;
257 struct lxc_inet6dev *inet6dev;
258 char bufinet4[INET_ADDRSTRLEN], bufinet6[INET6_ADDRSTRLEN];
259
260 netdev = it->elem;
261
262 TRACE("index: %zd", netdev->idx);
263 TRACE("ifindex: %d", netdev->ifindex);
264 switch (netdev->type) {
265 case LXC_NET_VETH:
266 TRACE("type: veth");
267 if (netdev->priv.veth_attr.pair[0] != '\0')
268 TRACE("veth pair: %s",
269 netdev->priv.veth_attr.pair);
270 if (netdev->priv.veth_attr.veth1[0] != '\0')
271 TRACE("veth1 : %s",
272 netdev->priv.veth_attr.veth1);
273 if (netdev->priv.veth_attr.ifindex > 0)
274 TRACE("host side ifindex for veth device: %d",
275 netdev->priv.veth_attr.ifindex);
276 break;
277 case LXC_NET_MACVLAN:
278 TRACE("type: macvlan");
279 if (netdev->priv.macvlan_attr.mode > 0) {
280 char *macvlan_mode;
281 macvlan_mode = lxc_macvlan_flag_to_mode(
282 netdev->priv.macvlan_attr.mode);
283 TRACE("macvlan mode: %s",
284 macvlan_mode ? macvlan_mode
285 : "(invalid mode)");
286 }
287 break;
288 case LXC_NET_VLAN:
289 TRACE("type: vlan");
290 TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
291 break;
292 case LXC_NET_PHYS:
293 TRACE("type: phys");
294 if (netdev->priv.phys_attr.ifindex > 0) {
295 TRACE("host side ifindex for phys device: %d",
296 netdev->priv.phys_attr.ifindex);
297 }
298 break;
299 case LXC_NET_EMPTY:
300 TRACE("type: empty");
301 break;
302 case LXC_NET_NONE:
303 TRACE("type: none");
304 break;
305 default:
306 ERROR("invalid network type %d", netdev->type);
307 return;
308 }
309
310 if (netdev->type != LXC_NET_EMPTY) {
311 TRACE("flags: %s",
312 netdev->flags == IFF_UP ? "up" : "none");
313 if (netdev->link[0] != '\0')
314 TRACE("link: %s", netdev->link);
315 if (netdev->name[0] != '\0')
316 TRACE("name: %s", netdev->name);
317 if (netdev->hwaddr)
318 TRACE("hwaddr: %s", netdev->hwaddr);
319 if (netdev->mtu)
320 TRACE("mtu: %s", netdev->mtu);
321 if (netdev->upscript)
322 TRACE("upscript: %s", netdev->upscript);
323 if (netdev->downscript)
324 TRACE("downscript: %s", netdev->downscript);
325
326 TRACE("ipv4 gateway auto: %s",
327 netdev->ipv4_gateway_auto ? "true" : "false");
328
329 if (netdev->ipv4_gateway) {
330 inet_ntop(AF_INET, netdev->ipv4_gateway,
331 bufinet4, sizeof(bufinet4));
332 TRACE("ipv4 gateway: %s", bufinet4);
333 }
334
335 lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
336 inet4dev = cur->elem;
337 inet_ntop(AF_INET, &inet4dev->addr, bufinet4,
338 sizeof(bufinet4));
339 TRACE("ipv4 addr: %s", bufinet4);
340 }
341
342 TRACE("ipv6 gateway auto: %s",
343 netdev->ipv6_gateway_auto ? "true" : "false");
344 if (netdev->ipv6_gateway) {
345 inet_ntop(AF_INET6, netdev->ipv6_gateway,
346 bufinet6, sizeof(bufinet6));
347 TRACE("ipv6 gateway: %s", bufinet6);
348 }
349 lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
350 inet6dev = cur->elem;
351 inet_ntop(AF_INET6, &inet6dev->addr, bufinet6,
352 sizeof(bufinet6));
353 TRACE("ipv6 addr: %s", bufinet6);
354 }
355 }
356 }
357 }
358
359 static void lxc_free_netdev(struct lxc_netdev *netdev)
360 {
361 struct lxc_list *cur, *next;
362
363 free(netdev->upscript);
364 free(netdev->downscript);
365 free(netdev->hwaddr);
366 free(netdev->mtu);
367
368 free(netdev->ipv4_gateway);
369 lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
370 lxc_list_del(cur);
371 free(cur->elem);
372 free(cur);
373 }
374
375 free(netdev->ipv6_gateway);
376 lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
377 lxc_list_del(cur);
378 free(cur->elem);
379 free(cur);
380 }
381
382 free(netdev);
383 }
384
385 bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx)
386 {
387 struct lxc_list *cur, *next;
388 struct lxc_netdev *netdev;
389 bool found = false;
390
391 lxc_list_for_each_safe(cur, &conf->network, next) {
392 netdev = cur->elem;
393 if (netdev->idx != idx)
394 continue;
395
396 lxc_list_del(cur);
397 found = true;
398 break;
399 }
400
401 if (!found)
402 return false;
403
404 lxc_free_netdev(netdev);
405 free(cur);
406
407 return true;
408 }
409
410 void lxc_free_networks(struct lxc_list *networks)
411 {
412 struct lxc_list *cur, *next;
413 struct lxc_netdev *netdev;
414
415 lxc_list_for_each_safe(cur, networks, next) {
416 netdev = cur->elem;
417 lxc_free_netdev(netdev);
418 free(cur);
419 }
420
421 /* prevent segfaults */
422 lxc_list_init(networks);
423 }
424
425 static struct macvlan_mode {
426 char *name;
427 int mode;
428 } macvlan_mode[] = {
429 { "private", MACVLAN_MODE_PRIVATE },
430 { "vepa", MACVLAN_MODE_VEPA },
431 { "bridge", MACVLAN_MODE_BRIDGE },
432 { "passthru", MACVLAN_MODE_PASSTHRU },
433 };
434
435 int lxc_macvlan_mode_to_flag(int *mode, const char *value)
436 {
437 size_t i;
438
439 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
440 if (strcmp(macvlan_mode[i].name, value))
441 continue;
442
443 *mode = macvlan_mode[i].mode;
444 return 0;
445 }
446
447 return -1;
448 }
449
450 char *lxc_macvlan_flag_to_mode(int mode)
451 {
452 size_t i;
453
454 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
455 if (macvlan_mode[i].mode == mode)
456 continue;
457
458 return macvlan_mode[i].name;
459 }
460
461 return NULL;
462 }
463
464 int set_config_string_item(char **conf_item, const char *value)
465 {
466 char *new_value;
467
468 if (lxc_config_value_empty(value)) {
469 free(*conf_item);
470 *conf_item = NULL;
471 return 0;
472 }
473
474 new_value = strdup(value);
475 if (!new_value) {
476 SYSERROR("failed to duplicate string \"%s\"", value);
477 return -1;
478 }
479
480 free(*conf_item);
481 *conf_item = new_value;
482 return 0;
483 }
484
485 int set_config_string_item_max(char **conf_item, const char *value, size_t max)
486 {
487 if (strlen(value) >= max) {
488 ERROR("%s is too long (>= %lu)", value, (unsigned long)max);
489 return -1;
490 }
491
492 return set_config_string_item(conf_item, value);
493 }
494
495 int set_config_path_item(char **conf_item, const char *value)
496 {
497 return set_config_string_item_max(conf_item, value, PATH_MAX);
498 }
499
500 int config_ip_prefix(struct in_addr *addr)
501 {
502 if (IN_CLASSA(addr->s_addr))
503 return 32 - IN_CLASSA_NSHIFT;
504 if (IN_CLASSB(addr->s_addr))
505 return 32 - IN_CLASSB_NSHIFT;
506 if (IN_CLASSC(addr->s_addr))
507 return 32 - IN_CLASSC_NSHIFT;
508
509 return 0;
510 }
511
512 int network_ifname(char *valuep, const char *value)
513 {
514 if (strlen(value) >= IFNAMSIZ) {
515 ERROR("Network devie name \"%s\" is too long (>= %zu)", value,
516 (size_t)IFNAMSIZ);
517 }
518
519 strcpy(valuep, value);
520 return 0;
521 }
522
523 int rand_complete_hwaddr(char *hwaddr)
524 {
525 const char hex[] = "0123456789abcdef";
526 char *curs = hwaddr;
527
528 #ifndef HAVE_RAND_R
529 randseed(true);
530 #else
531 unsigned int seed;
532
533 seed = randseed(false);
534 #endif
535 while (*curs != '\0' && *curs != '\n') {
536 if (*curs == 'x' || *curs == 'X') {
537 if (curs - hwaddr == 1) {
538 /* ensure address is unicast */
539 #ifdef HAVE_RAND_R
540 *curs = hex[rand_r(&seed) & 0x0E];
541 } else {
542 *curs = hex[rand_r(&seed) & 0x0F];
543 #else
544 *curs = hex[rand() & 0x0E];
545 } else {
546 *curs = hex[rand() & 0x0F];
547 #endif
548 }
549 }
550 curs++;
551 }
552 return 0;
553 }
554
555 bool lxc_config_net_hwaddr(const char *line)
556 {
557 unsigned index;
558 char tmp[7];
559
560 if (strncmp(line, "lxc.net", 7) != 0)
561 return false;
562 if (strncmp(line, "lxc.net.hwaddr", 14) == 0)
563 return true;
564 if (strncmp(line, "lxc.network.hwaddr", 18) == 0)
565 return true;
566 if (sscanf(line, "lxc.net.%u.%6s", &index, tmp) == 2 || sscanf(line, "lxc.network.%u.%6s", &index, tmp) == 2)
567 return strncmp(tmp, "hwaddr", 6) == 0;
568
569 return false;
570 }
571
572 /*
573 * If we find a lxc.net.[i].hwaddr or lxc.network.hwaddr in the original config
574 * file, we expand it in the unexpanded_config, so that after a save_config we
575 * store the hwaddr for re-use.
576 * This is only called when reading the config file, not when executing a
577 * lxc.include.
578 * 'x' and 'X' are substituted in-place.
579 */
580 void update_hwaddr(const char *line)
581 {
582 char *p;
583
584 line += lxc_char_left_gc(line, strlen(line));
585 if (line[0] == '#')
586 return;
587
588 if (!lxc_config_net_hwaddr(line))
589 return;
590
591 /* Let config_net_hwaddr raise the error. */
592 p = strchr(line, '=');
593 if (!p)
594 return;
595 p++;
596
597 while (isblank(*p))
598 p++;
599
600 if (!*p)
601 return;
602
603 rand_complete_hwaddr(p);
604 }
605
606 bool new_hwaddr(char *hwaddr)
607 {
608 int ret;
609
610 (void)randseed(true);
611
612 ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
613 rand() % 255, rand() % 255);
614 if (ret < 0 || ret >= 18) {
615 SYSERROR("Failed to call snprintf().");
616 return false;
617 }
618
619 return true;
620 }
621
622 int lxc_get_conf_str(char *retv, int inlen, const char *value)
623 {
624 if (!value)
625 return 0;
626 if (retv && inlen >= strlen(value) + 1)
627 strncpy(retv, value, strlen(value) + 1);
628
629 return strlen(value);
630 }
631
632 int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
633 {
634 if (!retv)
635 inlen = 0;
636 else
637 memset(retv, 0, inlen);
638
639 return snprintf(retv, inlen, "%d", v);
640 }
641
642 int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v)
643 {
644 if (!retv)
645 inlen = 0;
646 else
647 memset(retv, 0, inlen);
648
649 return snprintf(retv, inlen, "%"PRIu64, v);
650 }
651
652 bool parse_limit_value(const char **value, rlim_t *res)
653 {
654 char *endptr = NULL;
655
656 if (strncmp(*value, "unlimited", sizeof("unlimited") - 1) == 0) {
657 *res = RLIM_INFINITY;
658 *value += sizeof("unlimited") - 1;
659 return true;
660 }
661
662 errno = 0;
663 *res = strtoull(*value, &endptr, 10);
664 if (errno || !endptr)
665 return false;
666 *value = endptr;
667
668 return true;
669 }
670
671 static int lxc_container_name_to_pid(const char *lxcname_or_pid,
672 const char *lxcpath)
673 {
674 int ret;
675 signed long int pid;
676 char *err = NULL;
677
678 pid = strtol(lxcname_or_pid, &err, 10);
679 if (*err != '\0' || pid < 1) {
680 struct lxc_container *c;
681
682 c = lxc_container_new(lxcname_or_pid, lxcpath);
683 if (!c) {
684 ERROR("\"%s\" is not a valid pid nor a container name",
685 lxcname_or_pid);
686 return -1;
687 }
688
689 if (!c->may_control(c)) {
690 ERROR("Insufficient privileges to control container "
691 "\"%s\"", c->name);
692 lxc_container_put(c);
693 return -1;
694 }
695
696 pid = c->init_pid(c);
697 if (pid < 1) {
698 ERROR("Container \"%s\" is not running", c->name);
699 lxc_container_put(c);
700 return -1;
701 }
702
703 lxc_container_put(c);
704 }
705
706 ret = kill(pid, 0);
707 if (ret < 0) {
708 ERROR("%s - Failed to send signal to pid %d", strerror(errno),
709 (int)pid);
710 return -EPERM;
711 }
712
713 return pid;
714 }
715
716 int lxc_inherit_namespace(const char *lxcname_or_pid, const char *lxcpath,
717 const char *namespace)
718 {
719 int fd, pid;
720 char *dup, *lastslash;
721
722 lastslash = strrchr(lxcname_or_pid, '/');
723 if (lastslash) {
724 dup = strdup(lxcname_or_pid);
725 if (!dup)
726 return -ENOMEM;
727
728 dup[lastslash - lxcname_or_pid] = '\0';
729 pid = lxc_container_name_to_pid(lastslash + 1, dup);
730 free(dup);
731 } else {
732 pid = lxc_container_name_to_pid(lxcname_or_pid, lxcpath);
733 }
734
735 if (pid < 0)
736 return -EINVAL;
737
738 fd = lxc_preserve_ns(pid, namespace);
739 if (fd < 0)
740 return -EINVAL;
741
742 return fd;
743 }