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