68636dc2a426be2889ca6c4e4b408511956ccf24
[libvirt.git] / src / lxc / lxc_native.c
1 /*
2  * lxc_native.c: LXC native configuration import
3  *
4  * Copyright (c) 2014-2016 Red Hat, Inc.
5  * Copyright (c) 2013-2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <http://www.gnu.org/licenses/>.
20  *
21  * Author: Cedric Bosdonnat <cbosdonnat@suse.com>
22  */
23
24 #include <config.h>
25 #include <stdio.h>
26
27 #include "internal.h"
28 #include "lxc_container.h"
29 #include "lxc_native.h"
30 #include "util/viralloc.h"
31 #include "util/virfile.h"
32 #include "util/virlog.h"
33 #include "util/virstring.h"
34 #include "util/virconf.h"
35 #include "conf/domain_conf.h"
36
37 #define VIR_FROM_THIS VIR_FROM_LXC
38
39 VIR_LOG_INIT("lxc.lxc_native");
40
41 static virDomainFSDefPtr
42 lxcCreateFSDef(int type,
43                const char *src,
44                const char* dst,
45                bool readonly,
46                unsigned long long usage)
47 {
48     virDomainFSDefPtr def;
49
50     if (!(def = virDomainFSDefNew()))
51         return NULL;
52
53     def->type = type;
54     def->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH;
55     if (src && VIR_STRDUP(def->src->path, src) < 0)
56         goto error;
57     if (VIR_STRDUP(def->dst, dst) < 0)
58         goto error;
59     def->readonly = readonly;
60     def->usage = usage;
61
62     return def;
63
64  error:
65     virDomainFSDefFree(def);
66     return NULL;
67 }
68
69 typedef struct _lxcFstab lxcFstab;
70 typedef lxcFstab *lxcFstabPtr;
71 struct _lxcFstab {
72     lxcFstabPtr next;
73     char *src;
74     char *dst;
75     char *type;
76     char *options;
77 };
78
79 static void
80 lxcFstabFree(lxcFstabPtr fstab)
81 {
82     while (fstab) {
83         lxcFstabPtr next = NULL;
84         next = fstab->next;
85
86         VIR_FREE(fstab->src);
87         VIR_FREE(fstab->dst);
88         VIR_FREE(fstab->type);
89         VIR_FREE(fstab->options);
90         VIR_FREE(fstab);
91
92         fstab = next;
93     }
94 }
95
96 static char ** lxcStringSplit(const char *string)
97 {
98     char *tmp;
99     size_t i;
100     size_t ntokens = 0;
101     char **parts;
102     char **result = NULL;
103
104     if (VIR_STRDUP(tmp, string) < 0)
105         return NULL;
106
107     /* Replace potential \t by a space */
108     for (i = 0; tmp[i]; i++) {
109         if (tmp[i] == '\t')
110             tmp[i] = ' ';
111     }
112
113     if (!(parts = virStringSplit(tmp, " ", 0)))
114         goto error;
115
116     /* Append NULL element */
117     if (VIR_EXPAND_N(result, ntokens, 1) < 0)
118         goto error;
119
120     for (i = 0; parts[i]; i++) {
121         if (STREQ(parts[i], ""))
122             continue;
123
124         if (VIR_EXPAND_N(result, ntokens, 1) < 0)
125             goto error;
126
127         if (VIR_STRDUP(result[ntokens-2], parts[i]) < 0)
128             goto error;
129     }
130
131     VIR_FREE(tmp);
132     virStringListFree(parts);
133     return result;
134
135  error:
136     VIR_FREE(tmp);
137     virStringListFree(parts);
138     virStringListFree(result);
139     return NULL;
140 }
141
142 static lxcFstabPtr
143 lxcParseFstabLine(char *fstabLine)
144 {
145     lxcFstabPtr fstab = NULL;
146     char **parts;
147
148     if (!fstabLine || VIR_ALLOC(fstab) < 0)
149         return NULL;
150
151     if (!(parts = lxcStringSplit(fstabLine)))
152         goto error;
153
154     if (!parts[0] || !parts[1] || !parts[2] || !parts[3])
155         goto error;
156
157     if (VIR_STRDUP(fstab->src, parts[0]) < 0 ||
158             VIR_STRDUP(fstab->dst, parts[1]) < 0 ||
159             VIR_STRDUP(fstab->type, parts[2]) < 0 ||
160             VIR_STRDUP(fstab->options, parts[3]) < 0)
161         goto error;
162
163     virStringListFree(parts);
164
165     return fstab;
166
167  error:
168     lxcFstabFree(fstab);
169     virStringListFree(parts);
170     return NULL;
171 }
172
173 static int
174 lxcAddFSDef(virDomainDefPtr def,
175             int type,
176             const char *src,
177             const char *dst,
178             bool readonly,
179             unsigned long long usage)
180 {
181     virDomainFSDefPtr fsDef = NULL;
182
183     if (!(fsDef = lxcCreateFSDef(type, src, dst, readonly, usage)))
184         goto error;
185
186     if (VIR_EXPAND_N(def->fss, def->nfss, 1) < 0)
187         goto error;
188     def->fss[def->nfss - 1] = fsDef;
189
190     return 0;
191
192  error:
193     virDomainFSDefFree(fsDef);
194     return -1;
195 }
196
197 static int
198 lxcSetRootfs(virDomainDefPtr def,
199              virConfPtr properties)
200 {
201     int type = VIR_DOMAIN_FS_TYPE_MOUNT;
202     virConfValuePtr value;
203
204     if (!(value = virConfGetValue(properties, "lxc.rootfs")) ||
205         !value->str) {
206         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
207                        _("Missing lxc.rootfs configuration"));
208         return -1;
209     }
210
211     if (STRPREFIX(value->str, "/dev/"))
212         type = VIR_DOMAIN_FS_TYPE_BLOCK;
213
214     if (lxcAddFSDef(def, type, value->str, "/", false, 0) < 0)
215         return -1;
216
217     return 0;
218 }
219
220 static int
221 lxcConvertSize(const char *size, unsigned long long *value)
222 {
223     char *unit = NULL;
224
225     /* Split the string into value and unit */
226     if (virStrToLong_ull(size, &unit, 10, value) < 0)
227         goto error;
228
229     if (STREQ(unit, "%")) {
230         virReportError(VIR_ERR_INTERNAL_ERROR,
231                        _("can't convert relative size: '%s'"),
232                        size);
233         return -1;
234     } else {
235         if (virScaleInteger(value, unit, 1, ULLONG_MAX) < 0)
236             goto error;
237     }
238
239     return 0;
240
241  error:
242     virReportError(VIR_ERR_INTERNAL_ERROR,
243                    _("failed to convert size: '%s'"),
244                    size);
245     return -1;
246 }
247
248 static int
249 lxcAddFstabLine(virDomainDefPtr def, lxcFstabPtr fstab)
250 {
251     const char *src = NULL;
252     char *dst = NULL;
253     char **options = virStringSplit(fstab->options, ",", 0);
254     bool readonly;
255     int type = VIR_DOMAIN_FS_TYPE_MOUNT;
256     unsigned long long usage = 0;
257     int ret = -1;
258
259     if (!options)
260         return -1;
261
262     if (fstab->dst[0] != '/') {
263         if (virAsprintf(&dst, "/%s", fstab->dst) < 0)
264             goto cleanup;
265     } else {
266         if (VIR_STRDUP(dst, fstab->dst) < 0)
267             goto cleanup;
268     }
269
270     /* Check that we don't add basic mounts */
271     if (lxcIsBasicMountLocation(dst)) {
272         ret = 0;
273         goto cleanup;
274     }
275
276     if (STREQ(fstab->type, "tmpfs")) {
277         char *sizeStr = NULL;
278         size_t i;
279         type = VIR_DOMAIN_FS_TYPE_RAM;
280
281         for (i = 0; options[i]; i++) {
282             if ((sizeStr = STRSKIP(options[i], "size="))) {
283                 if (lxcConvertSize(sizeStr, &usage) < 0)
284                     goto cleanup;
285                 break;
286             }
287         }
288         if (!sizeStr) {
289             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
290                            _("missing tmpfs size, set the size option"));
291             goto cleanup;
292         }
293     } else {
294         src = fstab->src;
295     }
296
297     /* Is it a block device that needs special favor? */
298     if (STRPREFIX(fstab->src, "/dev/"))
299         type = VIR_DOMAIN_FS_TYPE_BLOCK;
300
301     /* Do we have ro in options? */
302     readonly = virStringListHasString((const char **) options, "ro");
303
304     if (lxcAddFSDef(def, type, src, dst, readonly, usage) < 0)
305         goto cleanup;
306
307     ret = 1;
308
309  cleanup:
310     VIR_FREE(dst);
311     virStringListFree(options);
312     return ret;
313 }
314
315 static int
316 lxcFstabWalkCallback(const char* name, virConfValuePtr value, void * data)
317 {
318     int ret = 0;
319     lxcFstabPtr fstabLine;
320     virDomainDefPtr def = data;
321
322     /* We only care about lxc.mount.entry lines */
323     if (STRNEQ(name, "lxc.mount.entry"))
324         return 0;
325
326     fstabLine = lxcParseFstabLine(value->str);
327
328     if (!fstabLine)
329         return -1;
330
331     if (lxcAddFstabLine(def, fstabLine) < 0)
332         ret = -1;
333
334     lxcFstabFree(fstabLine);
335     return ret;
336 }
337
338 static virDomainNetDefPtr
339 lxcCreateNetDef(const char *type,
340                 const char *linkdev,
341                 const char *mac,
342                 const char *flag,
343                 const char *macvlanmode,
344                 const char *name)
345 {
346     virDomainNetDefPtr net = NULL;
347     virMacAddr macAddr;
348
349     if (VIR_ALLOC(net) < 0)
350         goto error;
351
352     if (STREQ_NULLABLE(flag, "up"))
353         net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP;
354     else
355         net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN;
356
357     if (VIR_STRDUP(net->ifname_guest, name) < 0)
358         goto error;
359
360     if (mac && virMacAddrParse(mac, &macAddr) == 0)
361         net->mac = macAddr;
362
363     if (STREQ(type, "veth")) {
364         if (linkdev) {
365             net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
366             if (VIR_STRDUP(net->data.bridge.brname, linkdev) < 0)
367                 goto error;
368         } else {
369             net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
370         }
371     } else if (STREQ(type, "macvlan")) {
372         net->type = VIR_DOMAIN_NET_TYPE_DIRECT;
373
374         if (!linkdev || VIR_STRDUP(net->data.direct.linkdev, linkdev) < 0)
375             goto error;
376
377         if (!macvlanmode || STREQ(macvlanmode, "private"))
378             net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_PRIVATE;
379         else if (STREQ(macvlanmode, "vepa"))
380             net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_VEPA;
381         else if (STREQ(macvlanmode, "bridge"))
382             net->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_BRIDGE;
383         else
384             VIR_WARN("Unknown macvlan type: %s", macvlanmode);
385     }
386
387     return net;
388
389  error:
390     virDomainNetDefFree(net);
391     return NULL;
392 }
393
394 static virDomainHostdevDefPtr
395 lxcCreateHostdevDef(int mode, int type, const char *data)
396 {
397     virDomainHostdevDefPtr hostdev = virDomainHostdevDefNew(NULL);
398
399     if (!hostdev)
400         return NULL;
401
402     hostdev->mode = mode;
403     hostdev->source.caps.type = type;
404
405     if (type == VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET &&
406         VIR_STRDUP(hostdev->source.caps.u.net.ifname, data) < 0) {
407         virDomainHostdevDefFree(hostdev);
408         hostdev = NULL;
409     }
410
411     return hostdev;
412 }
413
414 typedef struct {
415     virDomainDefPtr def;
416     char *type;
417     char *link;
418     char *mac;
419     char *flag;
420     char *macvlanmode;
421     char *vlanid;
422     char *name;
423     virNetDevIPAddrPtr *ips;
424     size_t nips;
425     char *gateway_ipv4;
426     char *gateway_ipv6;
427     bool privnet;
428     size_t networks;
429 } lxcNetworkParseData;
430
431 static int
432 lxcAddNetworkRouteDefinition(const char *address,
433                              int family,
434                              virNetDevIPRoutePtr **routes,
435                              size_t *nroutes)
436 {
437     virNetDevIPRoutePtr route = NULL;
438     char *familyStr = NULL;
439     char *zero = NULL;
440
441     if (VIR_STRDUP(zero, family == AF_INET ? VIR_SOCKET_ADDR_IPV4_ALL
442                    : VIR_SOCKET_ADDR_IPV6_ALL) < 0)
443         goto error;
444
445     if (VIR_STRDUP(familyStr, family == AF_INET ? "ipv4" : "ipv6") < 0)
446         goto error;
447
448     if (!(route = virNetDevIPRouteCreate(_("Domain interface"), familyStr,
449                                          zero, NULL, address, 0, false,
450                                          0, false)))
451         goto error;
452
453     if (VIR_APPEND_ELEMENT(*routes, *nroutes, route) < 0)
454         goto error;
455
456     VIR_FREE(familyStr);
457     VIR_FREE(zero);
458
459     return 0;
460
461  error:
462     VIR_FREE(familyStr);
463     VIR_FREE(zero);
464     virNetDevIPRouteFree(route);
465     return -1;
466 }
467
468 static int
469 lxcAddNetworkDefinition(lxcNetworkParseData *data)
470 {
471     virDomainNetDefPtr net = NULL;
472     virDomainHostdevDefPtr hostdev = NULL;
473     bool isPhys, isVlan = false;
474     size_t i;
475
476     if ((data->type == NULL) || STREQ(data->type, "empty") ||
477          STREQ(data->type, "") ||  STREQ(data->type, "none"))
478         return 0;
479
480     isPhys = STREQ(data->type, "phys");
481     isVlan = STREQ(data->type, "vlan");
482     if (data->type != NULL && (isPhys || isVlan)) {
483         if (!data->link) {
484             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
485                            _("Missing 'link' attribute for NIC"));
486             goto error;
487         }
488         if (!(hostdev = lxcCreateHostdevDef(VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES,
489                                             VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET,
490                                             data->link)))
491             goto error;
492
493         /* This still requires the user to manually setup the vlan interface
494          * on the host */
495         if (isVlan && data->vlanid) {
496             VIR_FREE(hostdev->source.caps.u.net.ifname);
497             if (virAsprintf(&hostdev->source.caps.u.net.ifname,
498                             "%s.%s", data->link, data->vlanid) < 0)
499                 goto error;
500         }
501
502         hostdev->source.caps.u.net.ip.ips = data->ips;
503         hostdev->source.caps.u.net.ip.nips = data->nips;
504
505         if (data->gateway_ipv4 &&
506             lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET,
507                                          &hostdev->source.caps.u.net.ip.routes,
508                                          &hostdev->source.caps.u.net.ip.nroutes) < 0)
509                 goto error;
510
511         if (data->gateway_ipv6 &&
512             lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6,
513                                          &hostdev->source.caps.u.net.ip.routes,
514                                          &hostdev->source.caps.u.net.ip.nroutes) < 0)
515                 goto error;
516
517         if (VIR_EXPAND_N(data->def->hostdevs, data->def->nhostdevs, 1) < 0)
518             goto error;
519         data->def->hostdevs[data->def->nhostdevs - 1] = hostdev;
520     } else {
521         if (!(net = lxcCreateNetDef(data->type, data->link, data->mac,
522                                     data->flag, data->macvlanmode,
523                                     data->name)))
524             goto error;
525
526         net->guestIP.ips = data->ips;
527         net->guestIP.nips = data->nips;
528
529         if (data->gateway_ipv4 &&
530             lxcAddNetworkRouteDefinition(data->gateway_ipv4, AF_INET,
531                                          &net->guestIP.routes,
532                                          &net->guestIP.nroutes) < 0)
533                 goto error;
534
535         if (data->gateway_ipv6 &&
536             lxcAddNetworkRouteDefinition(data->gateway_ipv6, AF_INET6,
537                                          &net->guestIP.routes,
538                                          &net->guestIP.nroutes) < 0)
539                 goto error;
540
541         if (VIR_EXPAND_N(data->def->nets, data->def->nnets, 1) < 0)
542             goto error;
543         data->def->nets[data->def->nnets - 1] = net;
544     }
545
546     return 1;
547
548  error:
549     for (i = 0; i < data->nips; i++)
550         VIR_FREE(data->ips[i]);
551     VIR_FREE(data->ips);
552     virDomainNetDefFree(net);
553     virDomainHostdevDefFree(hostdev);
554     return -1;
555 }
556
557 static int
558 lxcNetworkWalkCallback(const char *name, virConfValuePtr value, void *data)
559 {
560     lxcNetworkParseData *parseData = data;
561     int status;
562
563     if (STREQ(name, "lxc.network.type")) {
564         /* Store the previous NIC */
565         status = lxcAddNetworkDefinition(parseData);
566
567         if (status < 0)
568             return -1;
569         else if (status > 0)
570             parseData->networks++;
571         else if (parseData->type != NULL && STREQ(parseData->type, "none"))
572             parseData->privnet = false;
573
574         /* Start a new network interface config */
575         parseData->type = NULL;
576         parseData->link = NULL;
577         parseData->mac = NULL;
578         parseData->flag = NULL;
579         parseData->macvlanmode = NULL;
580         parseData->vlanid = NULL;
581         parseData->name = NULL;
582
583         parseData->ips = NULL;
584         parseData->nips = 0;
585
586         /* Keep the new value */
587         parseData->type = value->str;
588     }
589     else if (STREQ(name, "lxc.network.link"))
590         parseData->link = value->str;
591     else if (STREQ(name, "lxc.network.hwaddr"))
592         parseData->mac = value->str;
593     else if (STREQ(name, "lxc.network.flags"))
594         parseData->flag = value->str;
595     else if (STREQ(name, "lxc.network.macvlan.mode"))
596         parseData->macvlanmode = value->str;
597     else if (STREQ(name, "lxc.network.vlan.id"))
598         parseData->vlanid = value->str;
599     else if (STREQ(name, "lxc.network.name"))
600         parseData->name = value->str;
601     else if (STREQ(name, "lxc.network.ipv4") ||
602              STREQ(name, "lxc.network.ipv6")) {
603         int family = AF_INET;
604         char **ipparts = NULL;
605         virNetDevIPAddrPtr ip = NULL;
606
607         if (VIR_ALLOC(ip) < 0)
608             return -1;
609
610         if (STREQ(name, "lxc.network.ipv6"))
611             family = AF_INET6;
612
613         ipparts = virStringSplit(value->str, "/", 2);
614         if (virStringListLength((const char * const *)ipparts) != 2 ||
615             virSocketAddrParse(&ip->address, ipparts[0], family) < 0 ||
616             virStrToLong_ui(ipparts[1], NULL, 10, &ip->prefix) < 0) {
617
618             virReportError(VIR_ERR_INVALID_ARG,
619                            _("Invalid CIDR address: '%s'"), value->str);
620
621             virStringListFree(ipparts);
622             VIR_FREE(ip);
623             return -1;
624         }
625
626         virStringListFree(ipparts);
627
628         if (VIR_APPEND_ELEMENT(parseData->ips, parseData->nips, ip) < 0) {
629             VIR_FREE(ip);
630             return -1;
631         }
632     } else if (STREQ(name, "lxc.network.ipv4.gateway")) {
633         parseData->gateway_ipv4 = value->str;
634     } else if (STREQ(name, "lxc.network.ipv6.gateway")) {
635         parseData->gateway_ipv6 = value->str;
636     } else if (STRPREFIX(name, "lxc.network")) {
637         VIR_WARN("Unhandled network property: %s = %s",
638                  name,
639                  value->str);
640     }
641
642     return 0;
643 }
644
645 static int
646 lxcConvertNetworkSettings(virDomainDefPtr def, virConfPtr properties)
647 {
648     int status;
649     int result = -1;
650     size_t i;
651     lxcNetworkParseData data = {def, NULL, NULL, NULL, NULL,
652                                 NULL, NULL, NULL, NULL, 0,
653                                 NULL, NULL, true, 0};
654
655     if (virConfWalk(properties, lxcNetworkWalkCallback, &data) < 0)
656         goto error;
657
658
659     /* Add the last network definition found */
660     status = lxcAddNetworkDefinition(&data);
661
662     if (status < 0)
663         goto error;
664     else if (status > 0)
665         data.networks++;
666     else if (data.type != NULL && STREQ(data.type, "none"))
667         data.privnet = false;
668
669     if (data.networks == 0 && data.privnet) {
670         /* When no network type is provided LXC only adds loopback */
671         def->features[VIR_DOMAIN_FEATURE_PRIVNET] = VIR_TRISTATE_SWITCH_ON;
672     }
673     result = 0;
674
675     return result;
676
677  error:
678     for (i = 0; i < data.nips; i++)
679         VIR_FREE(data.ips[i]);
680     VIR_FREE(data.ips);
681     return -1;
682 }
683
684 static int
685 lxcCreateConsoles(virDomainDefPtr def, virConfPtr properties)
686 {
687     virConfValuePtr value;
688     int nbttys = 0;
689     virDomainChrDefPtr console;
690     size_t i;
691
692     if (!(value = virConfGetValue(properties, "lxc.tty")) || !value->str)
693         return 0;
694
695     if (virStrToLong_i(value->str, NULL, 10, &nbttys) < 0) {
696         virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to parse int: '%s'"),
697                        value->str);
698         return -1;
699     }
700
701     if (VIR_ALLOC_N(def->consoles, nbttys) < 0)
702         return -1;
703
704     def->nconsoles = nbttys;
705     for (i = 0; i < nbttys; i++) {
706         if (!(console = virDomainChrDefNew(NULL)))
707             goto error;
708
709         console->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
710         console->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LXC;
711         console->target.port = i;
712         console->source->type = VIR_DOMAIN_CHR_TYPE_PTY;
713
714         def->consoles[i] = console;
715     }
716
717     return 0;
718
719  error:
720     virDomainChrDefFree(console);
721     return -1;
722 }
723
724 static int
725 lxcIdmapWalkCallback(const char *name, virConfValuePtr value, void *data)
726 {
727     virDomainDefPtr def = data;
728     virDomainIdMapEntryPtr idmap = NULL;
729     char type;
730     unsigned long start, target, count;
731
732     if (STRNEQ(name, "lxc.id_map") || !value->str)
733         return 0;
734
735     if (sscanf(value->str, "%c %lu %lu %lu", &type,
736                &target, &start, &count) != 4) {
737         virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid lxc.id_map: '%s'"),
738                        value->str);
739         return -1;
740     }
741
742     if (type == 'u') {
743         if (VIR_EXPAND_N(def->idmap.uidmap, def->idmap.nuidmap, 1) < 0)
744             return -1;
745         idmap = &def->idmap.uidmap[def->idmap.nuidmap - 1];
746     } else if (type == 'g') {
747         if (VIR_EXPAND_N(def->idmap.gidmap, def->idmap.ngidmap, 1) < 0)
748             return -1;
749         idmap = &def->idmap.gidmap[def->idmap.ngidmap - 1];
750     } else {
751         return -1;
752     }
753
754     idmap->start = start;
755     idmap->target = target;
756     idmap->count = count;
757
758     return 0;
759 }
760
761 static int
762 lxcSetMemTune(virDomainDefPtr def, virConfPtr properties)
763 {
764     virConfValuePtr value;
765     unsigned long long size = 0;
766
767     if ((value = virConfGetValue(properties,
768                 "lxc.cgroup.memory.limit_in_bytes")) &&
769             value->str && STRNEQ(value->str, "-1")) {
770         if (lxcConvertSize(value->str, &size) < 0)
771             return -1;
772         size = size / 1024;
773         virDomainDefSetMemoryTotal(def, size);
774         def->mem.hard_limit = virMemoryLimitTruncate(size);
775     }
776
777     if ((value = virConfGetValue(properties,
778                 "lxc.cgroup.memory.soft_limit_in_bytes")) &&
779             value->str && STRNEQ(value->str, "-1")) {
780         if (lxcConvertSize(value->str, &size) < 0)
781             return -1;
782
783         def->mem.soft_limit = virMemoryLimitTruncate(size / 1024);
784     }
785
786     if ((value = virConfGetValue(properties,
787                 "lxc.cgroup.memory.memsw.limit_in_bytes")) &&
788             value->str && STRNEQ(value->str, "-1")) {
789         if (lxcConvertSize(value->str, &size) < 0)
790             return -1;
791
792         def->mem.swap_hard_limit = virMemoryLimitTruncate(size / 1024);
793     }
794     return 0;
795 }
796
797 static int
798 lxcSetCpuTune(virDomainDefPtr def, virConfPtr properties)
799 {
800     virConfValuePtr value;
801
802     if ((value = virConfGetValue(properties, "lxc.cgroup.cpu.shares")) &&
803             value->str) {
804         if (virStrToLong_ull(value->str, NULL, 10, &def->cputune.shares) < 0)
805             goto error;
806         def->cputune.sharesSpecified = true;
807     }
808
809     if ((value = virConfGetValue(properties,
810                                  "lxc.cgroup.cpu.cfs_quota_us")) &&
811             value->str && virStrToLong_ll(value->str, NULL, 10,
812                                           &def->cputune.quota) < 0)
813         goto error;
814
815     if ((value = virConfGetValue(properties,
816                                  "lxc.cgroup.cpu.cfs_period_us")) &&
817             value->str && virStrToLong_ull(value->str, NULL, 10,
818                                            &def->cputune.period) < 0)
819         goto error;
820
821     return 0;
822
823  error:
824     virReportError(VIR_ERR_INTERNAL_ERROR,
825                    _("failed to parse integer: '%s'"), value->str);
826     return -1;
827 }
828
829 static int
830 lxcSetCpusetTune(virDomainDefPtr def, virConfPtr properties)
831 {
832     virConfValuePtr value;
833     virBitmapPtr nodeset = NULL;
834
835     if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.cpus")) &&
836             value->str) {
837         if (virBitmapParse(value->str, &def->cpumask,
838                            VIR_DOMAIN_CPUMASK_LEN) < 0)
839             return -1;
840
841         def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC;
842     }
843
844     if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.mems")) &&
845         value->str) {
846         if (virBitmapParse(value->str, &nodeset, VIR_DOMAIN_CPUMASK_LEN) < 0)
847             return -1;
848         if (virDomainNumatuneSet(def->numa,
849                                  def->placement_mode ==
850                                  VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC,
851                                  VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC,
852                                  VIR_DOMAIN_NUMATUNE_MEM_STRICT,
853                                  nodeset) < 0) {
854             virBitmapFree(nodeset);
855             return -1;
856         }
857         virBitmapFree(nodeset);
858     }
859
860     return 0;
861 }
862
863 static int
864 lxcBlkioDeviceWalkCallback(const char *name, virConfValuePtr value, void *data)
865 {
866     char **parts = NULL;
867     virBlkioDevicePtr device = NULL;
868     virDomainDefPtr def = data;
869     size_t i = 0;
870     char *path = NULL;
871     int ret = -1;
872
873     if (!STRPREFIX(name, "lxc.cgroup.blkio.") ||
874             STREQ(name, "lxc.cgroup.blkio.weight")|| !value->str)
875         return 0;
876
877     if (!(parts = lxcStringSplit(value->str)))
878         return -1;
879
880     if (!parts[0] || !parts[1]) {
881         virReportError(VIR_ERR_INTERNAL_ERROR,
882                        _("invalid %s value: '%s'"),
883                        name, value->str);
884         goto cleanup;
885     }
886
887     if (virAsprintf(&path, "/dev/block/%s", parts[0]) < 0)
888         goto cleanup;
889
890     /* Do we already have a device definition for this path?
891      * Get that device or create a new one */
892     for (i = 0; !device && i < def->blkio.ndevices; i++) {
893         if (STREQ(def->blkio.devices[i].path, path))
894             device = &def->blkio.devices[i];
895     }
896     if (!device) {
897         if (VIR_EXPAND_N(def->blkio.devices, def->blkio.ndevices, 1) < 0)
898             goto cleanup;
899         device = &def->blkio.devices[def->blkio.ndevices - 1];
900         device->path = path;
901         path = NULL;
902     }
903
904     /* Set the value */
905     if (STREQ(name, "lxc.cgroup.blkio.device_weight")) {
906         if (virStrToLong_ui(parts[1], NULL, 10, &device->weight) < 0) {
907             virReportError(VIR_ERR_INTERNAL_ERROR,
908                            _("failed to parse device weight: '%s'"), parts[1]);
909             goto cleanup;
910         }
911     } else if (STREQ(name, "lxc.cgroup.blkio.throttle.read_bps_device")) {
912         if (virStrToLong_ull(parts[1], NULL, 10, &device->rbps) < 0) {
913             virReportError(VIR_ERR_INTERNAL_ERROR,
914                            _("failed to parse read_bps_device: '%s'"),
915                            parts[1]);
916             goto cleanup;
917         }
918     } else if (STREQ(name, "lxc.cgroup.blkio.throttle.write_bps_device")) {
919         if (virStrToLong_ull(parts[1], NULL, 10, &device->wbps) < 0) {
920             virReportError(VIR_ERR_INTERNAL_ERROR,
921                            _("failed to parse write_bps_device: '%s'"),
922                            parts[1]);
923             goto cleanup;
924         }
925     } else if (STREQ(name, "lxc.cgroup.blkio.throttle.read_iops_device")) {
926         if (virStrToLong_ui(parts[1], NULL, 10, &device->riops) < 0) {
927             virReportError(VIR_ERR_INTERNAL_ERROR,
928                            _("failed to parse read_iops_device: '%s'"),
929                            parts[1]);
930             goto cleanup;
931         }
932     } else if (STREQ(name, "lxc.cgroup.blkio.throttle.write_iops_device")) {
933         if (virStrToLong_ui(parts[1], NULL, 10, &device->wiops) < 0) {
934             virReportError(VIR_ERR_INTERNAL_ERROR,
935                            _("failed to parse write_iops_device: '%s'"),
936                            parts[1]);
937             goto cleanup;
938         }
939     } else {
940         VIR_WARN("Unhandled blkio tune config: %s", name);
941     }
942
943     ret = 0;
944
945  cleanup:
946     virStringListFree(parts);
947     VIR_FREE(path);
948
949     return ret;
950 }
951
952 static int
953 lxcSetBlkioTune(virDomainDefPtr def, virConfPtr properties)
954 {
955     virConfValuePtr value;
956
957     if ((value = virConfGetValue(properties, "lxc.cgroup.blkio.weight")) &&
958             value->str && virStrToLong_ui(value->str, NULL, 10,
959                                           &def->blkio.weight) < 0) {
960         virReportError(VIR_ERR_INTERNAL_ERROR,
961                        _("failed to parse integer: '%s'"), value->str);
962         return -1;
963     }
964
965     if (virConfWalk(properties, lxcBlkioDeviceWalkCallback, def) < 0)
966         return -1;
967
968     return 0;
969 }
970
971 static void
972 lxcSetCapDrop(virDomainDefPtr def, virConfPtr properties)
973 {
974     virConfValuePtr value;
975     char **toDrop = NULL;
976     const char *capString;
977     size_t i;
978
979     if ((value = virConfGetValue(properties, "lxc.cap.drop")) && value->str)
980         toDrop = virStringSplit(value->str, " ", 0);
981
982     for (i = 0; i < VIR_DOMAIN_CAPS_FEATURE_LAST; i++) {
983         capString = virDomainCapsFeatureTypeToString(i);
984         if (toDrop != NULL &&
985             virStringListHasString((const char **) toDrop, capString))
986             def->caps_features[i] = VIR_TRISTATE_SWITCH_OFF;
987     }
988
989     def->features[VIR_DOMAIN_FEATURE_CAPABILITIES] = VIR_DOMAIN_CAPABILITIES_POLICY_ALLOW;
990
991     virStringListFree(toDrop);
992 }
993
994 virDomainDefPtr
995 lxcParseConfigString(const char *config,
996                      virCapsPtr caps,
997                      virDomainXMLOptionPtr xmlopt)
998 {
999     virDomainDefPtr vmdef = NULL;
1000     virConfPtr properties = NULL;
1001     virConfValuePtr value;
1002
1003     if (!(properties = virConfReadString(config, VIR_CONF_FLAG_LXC_FORMAT)))
1004         return NULL;
1005
1006     if (!(vmdef = virDomainDefNew()))
1007         goto error;
1008
1009     if (virUUIDGenerate(vmdef->uuid) < 0) {
1010         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1011                        _("failed to generate uuid"));
1012         goto error;
1013     }
1014
1015     vmdef->id = -1;
1016     virDomainDefSetMemoryTotal(vmdef, 64 * 1024);
1017
1018     vmdef->onReboot = VIR_DOMAIN_LIFECYCLE_ACTION_RESTART;
1019     vmdef->onCrash = VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY;
1020     vmdef->onPoweroff = VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY;
1021     vmdef->virtType = VIR_DOMAIN_VIRT_LXC;
1022
1023     /* Value not handled by the LXC driver, setting to
1024      * minimum required to make XML parsing pass */
1025     if (virDomainDefSetVcpusMax(vmdef, 1, xmlopt) < 0)
1026         goto error;
1027
1028     if (virDomainDefSetVcpus(vmdef, 1) < 0)
1029         goto error;
1030
1031     vmdef->nfss = 0;
1032     vmdef->os.type = VIR_DOMAIN_OSTYPE_EXE;
1033
1034     if ((value = virConfGetValue(properties, "lxc.arch")) && value->str) {
1035         virArch arch = virArchFromString(value->str);
1036         if (arch == VIR_ARCH_NONE && STREQ(value->str, "x86"))
1037             arch = VIR_ARCH_I686;
1038         else if (arch == VIR_ARCH_NONE && STREQ(value->str, "amd64"))
1039             arch = VIR_ARCH_X86_64;
1040         vmdef->os.arch = arch;
1041     }
1042
1043     if (VIR_STRDUP(vmdef->os.init, "/sbin/init") < 0)
1044         goto error;
1045
1046     if (!(value = virConfGetValue(properties, "lxc.utsname")) ||
1047             !value->str || (VIR_STRDUP(vmdef->name, value->str) < 0))
1048         goto error;
1049     if (!vmdef->name && (VIR_STRDUP(vmdef->name, "unnamed") < 0))
1050         goto error;
1051
1052     if (lxcSetRootfs(vmdef, properties) < 0)
1053         goto error;
1054
1055     /* Look for fstab: we shouldn't have it */
1056     if (virConfGetValue(properties, "lxc.mount")) {
1057         virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
1058                        _("lxc.mount found, use lxc.mount.entry lines instead"));
1059         goto error;
1060     }
1061
1062     /* Loop over lxc.mount.entry to add filesystem devices for them */
1063     if (virConfWalk(properties, lxcFstabWalkCallback, vmdef) < 0)
1064         goto error;
1065
1066     /* Network configuration */
1067     if (lxcConvertNetworkSettings(vmdef, properties) < 0)
1068         goto error;
1069
1070     /* Consoles */
1071     if (lxcCreateConsoles(vmdef, properties) < 0)
1072         goto error;
1073
1074     /* lxc.id_map */
1075     if (virConfWalk(properties, lxcIdmapWalkCallback, vmdef) < 0)
1076         goto error;
1077
1078     /* lxc.cgroup.memory.* */
1079     if (lxcSetMemTune(vmdef, properties) < 0)
1080         goto error;
1081
1082     /* lxc.cgroup.cpu.* */
1083     if (lxcSetCpuTune(vmdef, properties) < 0)
1084         goto error;
1085
1086     /* lxc.cgroup.cpuset.* */
1087     if (lxcSetCpusetTune(vmdef, properties) < 0)
1088         goto error;
1089
1090     /* lxc.cgroup.blkio.* */
1091     if (lxcSetBlkioTune(vmdef, properties) < 0)
1092         goto error;
1093
1094     /* lxc.cap.drop */
1095     lxcSetCapDrop(vmdef, properties);
1096
1097     if (virDomainDefPostParse(vmdef, caps, VIR_DOMAIN_DEF_PARSE_ABI_UPDATE,
1098                               xmlopt, NULL) < 0)
1099         goto cleanup;
1100
1101     goto cleanup;
1102
1103  error:
1104     virDomainDefFree(vmdef);
1105     vmdef = NULL;
1106
1107  cleanup:
1108     virConfFree(properties);
1109
1110     return vmdef;
1111 }