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