]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/confile_utils.c
tree-wide: fix mode of some files
[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. */
a8b1ac78 65 if (*slide != 'u' && *slide != 'g') {
f37d1c22 66 ERROR("Invalid id mapping type: %c", *slide);
0b843d35 67 goto on_error;
a8b1ac78
TA
68 }
69
0b843d35
CB
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
f37d1c22 83 /* Mark beginning of nsid. */
0b843d35
CB
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;
f37d1c22 90 /* Mark end of nsid. */
0b843d35
CB
91 *slide = '\0';
92
f37d1c22 93 /* Parse nsid. */
a8b1ac78 94 if (lxc_safe_ulong(window, &tmp_nsid) < 0) {
f37d1c22 95 ERROR("Failed to parse nsid: %s", window);
0b843d35 96 goto on_error;
a8b1ac78 97 }
0b843d35
CB
98
99 /* Move beyond \0. */
100 slide++;
0b843d35
CB
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;
f37d1c22 116 /* Mark end of nsid. */
0b843d35
CB
117 *slide = '\0';
118
119 /* Parse hostid. */
a8b1ac78 120 if (lxc_safe_ulong(window, &tmp_hostid) < 0) {
f37d1c22 121 ERROR("Failed to parse hostid: %s", window);
0b843d35 122 goto on_error;
a8b1ac78 123 }
0b843d35
CB
124
125 /* Move beyond \0. */
126 slide++;
0b843d35
CB
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
f37d1c22 144 * is no trailing garbage and if there is, error out.
0b843d35
CB
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. */
a8b1ac78 152 if (lxc_safe_ulong(window, &tmp_range) < 0) {
f37d1c22 153 ERROR("Failed to parse id mapping range: %s", window);
0b843d35 154 goto on_error;
a8b1ac78 155 }
0b843d35
CB
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
165on_error:
166 free(dup);
167
168 return ret;
169}
663e9916
CB
170
171bool lxc_config_value_empty(const char *value)
172{
173 if (value && strlen(value) > 0)
174 return false;
175
176 return true;
177}
ce2f5ae8 178
c302b476 179struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail)
ce2f5ae8
CB
180{
181 struct lxc_list *newlist;
182 struct lxc_netdev *netdev = NULL;
ce2f5ae8
CB
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
c302b476
CB
206 if (tail)
207 lxc_list_add_tail(networks, newlist);
208 else
209 lxc_list_add(networks, newlist);
ce2f5ae8
CB
210 return netdev;
211}
1ed6ba91 212
c302b476
CB
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 */
216struct 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
1ed6ba91
CB
240void 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) {
9b0df30f
CB
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
1ed6ba91
CB
260 netdev = it->elem;
261
c302b476 262 TRACE("index: %zd", netdev->idx);
7a582518 263 TRACE("ifindex: %d", netdev->ifindex);
1ed6ba91
CB
264 switch (netdev->type) {
265 case LXC_NET_VETH:
266 TRACE("type: veth");
de4855a8 267 if (netdev->priv.veth_attr.pair[0] != '\0')
9b0df30f
CB
268 TRACE("veth pair: %s",
269 netdev->priv.veth_attr.pair);
8ce727fc
CB
270 if (netdev->priv.veth_attr.veth1[0] != '\0')
271 TRACE("veth1 : %s",
272 netdev->priv.veth_attr.veth1);
d952b351
CB
273 if (netdev->priv.veth_attr.ifindex > 0)
274 TRACE("host side ifindex for veth device: %d",
275 netdev->priv.veth_attr.ifindex);
1ed6ba91
CB
276 break;
277 case LXC_NET_MACVLAN:
278 TRACE("type: macvlan");
9b0df30f
CB
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 }
1ed6ba91
CB
287 break;
288 case LXC_NET_VLAN:
289 TRACE("type: vlan");
9b0df30f 290 TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
1ed6ba91
CB
291 break;
292 case LXC_NET_PHYS:
293 TRACE("type: phys");
b809f232
CB
294 if (netdev->priv.phys_attr.ifindex > 0) {
295 TRACE("host side ifindex for phys device: %d",
296 netdev->priv.phys_attr.ifindex);
297 }
1ed6ba91
CB
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
9b0df30f
CB
310 if (netdev->type != LXC_NET_EMPTY) {
311 TRACE("flags: %s",
312 netdev->flags == IFF_UP ? "up" : "none");
de4855a8 313 if (netdev->link[0] != '\0')
9b0df30f 314 TRACE("link: %s", netdev->link);
de4855a8 315 if (netdev->name[0] != '\0')
9b0df30f
CB
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 }
1ed6ba91
CB
356 }
357}
519df1c1 358
e5d2fd7c
CB
359static void lxc_free_netdev(struct lxc_netdev *netdev)
360{
361 struct lxc_list *cur, *next;
362
e5d2fd7c
CB
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
519df1c1
CB
385bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx)
386{
e5d2fd7c 387 struct lxc_list *cur, *next;
519df1c1
CB
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
e5d2fd7c 404 lxc_free_netdev(netdev);
519df1c1
CB
405 free(cur);
406
407 return true;
408}
e5d2fd7c 409
c302b476 410void lxc_free_networks(struct lxc_list *networks)
e5d2fd7c
CB
411{
412 struct lxc_list *cur, *next;
413 struct lxc_netdev *netdev;
414
c302b476 415 lxc_list_for_each_safe(cur, networks, next) {
e5d2fd7c
CB
416 netdev = cur->elem;
417 lxc_free_netdev(netdev);
418 free(cur);
419 }
420
421 /* prevent segfaults */
c302b476 422 lxc_list_init(networks);
e5d2fd7c 423}
9b0df30f
CB
424
425static 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
435int 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
450char *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}
f9373e40
CB
463
464int 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
485int 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
495int 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
500int 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
de4855a8 512int network_ifname(char *valuep, const char *value)
f9373e40 513{
de4855a8
CB
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;
f9373e40
CB
521}
522
523int 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
ce4be612 555bool lxc_config_net_hwaddr(const char *line)
556{
44047b2b
FA
557 unsigned index;
558 char tmp[7];
ce4be612 559
560 if (strncmp(line, "lxc.net", 7) != 0)
561 return false;
44047b2b 562 if (strncmp(line, "lxc.net.hwaddr", 14) == 0)
ce4be612 563 return true;
44047b2b 564 if (strncmp(line, "lxc.network.hwaddr", 18) == 0)
ce4be612 565 return true;
44047b2b
FA
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;
ce4be612 568
ce4be612 569 return false;
570}
571
f9373e40 572/*
ae1dc8b4 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.
f9373e40
CB
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 */
580void 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
ae1dc8b4 588 if (!lxc_config_net_hwaddr(line))
f9373e40
CB
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
606bool 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}
953fe44f
CB
621
622int lxc_get_conf_str(char *retv, int inlen, const char *value)
623{
d3bdf12c
CB
624 size_t value_len;
625
953fe44f
CB
626 if (!value)
627 return 0;
d3bdf12c
CB
628
629 value_len = strlen(value);
630 if (retv && inlen >= value_len + 1)
631 memcpy(retv, value, value_len + 1);
953fe44f
CB
632
633 return strlen(value);
634}
635
636int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
637{
638 if (!retv)
639 inlen = 0;
640 else
641 memset(retv, 0, inlen);
642
643 return snprintf(retv, inlen, "%d", v);
644}
240d4b74 645
885766f5
CB
646int lxc_get_conf_size_t(struct lxc_conf *c, char *retv, int inlen, size_t v)
647{
648 if (!retv)
649 inlen = 0;
650 else
651 memset(retv, 0, inlen);
652
653 return snprintf(retv, inlen, "%zu", v);
654}
655
2ea479c9
CB
656int lxc_get_conf_uint64(struct lxc_conf *c, char *retv, int inlen, uint64_t v)
657{
658 if (!retv)
659 inlen = 0;
660 else
661 memset(retv, 0, inlen);
662
663 return snprintf(retv, inlen, "%"PRIu64, v);
664}
665
71460831 666bool parse_limit_value(const char **value, rlim_t *res)
240d4b74 667{
668 char *endptr = NULL;
669
670 if (strncmp(*value, "unlimited", sizeof("unlimited") - 1) == 0) {
671 *res = RLIM_INFINITY;
672 *value += sizeof("unlimited") - 1;
673 return true;
674 }
675
676 errno = 0;
71460831 677 *res = strtoull(*value, &endptr, 10);
240d4b74 678 if (errno || !endptr)
679 return false;
680 *value = endptr;
681
682 return true;
683}
28d9e29e
CB
684
685static int lxc_container_name_to_pid(const char *lxcname_or_pid,
686 const char *lxcpath)
687{
688 int ret;
689 signed long int pid;
690 char *err = NULL;
691
692 pid = strtol(lxcname_or_pid, &err, 10);
693 if (*err != '\0' || pid < 1) {
694 struct lxc_container *c;
695
696 c = lxc_container_new(lxcname_or_pid, lxcpath);
697 if (!c) {
698 ERROR("\"%s\" is not a valid pid nor a container name",
699 lxcname_or_pid);
700 return -1;
701 }
702
703 if (!c->may_control(c)) {
704 ERROR("Insufficient privileges to control container "
705 "\"%s\"", c->name);
706 lxc_container_put(c);
707 return -1;
708 }
709
710 pid = c->init_pid(c);
711 if (pid < 1) {
712 ERROR("Container \"%s\" is not running", c->name);
713 lxc_container_put(c);
714 return -1;
715 }
716
717 lxc_container_put(c);
718 }
719
720 ret = kill(pid, 0);
721 if (ret < 0) {
722 ERROR("%s - Failed to send signal to pid %d", strerror(errno),
723 (int)pid);
724 return -EPERM;
725 }
726
727 return pid;
728}
729
730int lxc_inherit_namespace(const char *lxcname_or_pid, const char *lxcpath,
731 const char *namespace)
732{
733 int fd, pid;
734 char *dup, *lastslash;
735
736 lastslash = strrchr(lxcname_or_pid, '/');
737 if (lastslash) {
738 dup = strdup(lxcname_or_pid);
739 if (!dup)
740 return -ENOMEM;
741
71649566
L
742 dup[lastslash - lxcname_or_pid] = '\0';
743 pid = lxc_container_name_to_pid(lastslash + 1, dup);
28d9e29e
CB
744 free(dup);
745 } else {
746 pid = lxc_container_name_to_pid(lxcname_or_pid, lxcpath);
747 }
748
749 if (pid < 0)
750 return -EINVAL;
751
752 fd = lxc_preserve_ns(pid, namespace);
753 if (fd < 0)
754 return -EINVAL;
755
756 return fd;
757}