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