1 /*
2  * openvz_conf.c: config functions for managing OpenVZ VEs
3  *
4  * Copyright (C) 2010-2012, 2014 Red Hat, Inc.
5  * Copyright (C) 2006, 2007 Binary Karma
6  * Copyright (C) 2006 Shuveb Hussain
7  * Copyright (C) 2007 Anoop Joe Cyriac
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library.  If not, see
21  * <http://www.gnu.org/licenses/>.
22  */
23 
24 #include <config.h>
25 
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <time.h>
31 #include <sys/stat.h>
32 
33 #include "virerror.h"
34 #include "openvz_conf.h"
35 #include "openvz_util.h"
36 #include "viruuid.h"
37 #include "virbuffer.h"
38 #include "viralloc.h"
39 #include "virfile.h"
40 #include "vircommand.h"
41 #include "virstring.h"
42 #include "virhostcpu.h"
43 #include "virutil.h"
44 
45 #define VIR_FROM_THIS VIR_FROM_OPENVZ
46 
47 static char *openvzLocateConfDir(void);
48 static int openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len);
49 static int openvzAssignUUIDs(void);
50 static int openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext);
51 
52 openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
53 
54 int
strtoI(const char * str)55 strtoI(const char *str)
56 {
57     int val;
58 
59     if (virStrToLong_i(str, NULL, 10, &val) < 0)
60         return 0;
61 
62     return val;
63 }
64 
65 
66 static int
openvzExtractVersionInfo(const char * cmdstr,int * retversion)67 openvzExtractVersionInfo(const char *cmdstr, int *retversion)
68 {
69     int ret = -1;
70     unsigned long version;
71     char *help = NULL;
72     char *tmp;
73     virCommand *cmd = virCommandNewArgList(cmdstr, "--help", NULL);
74 
75     if (retversion)
76         *retversion = 0;
77 
78     virCommandAddEnvString(cmd, "LC_ALL=C");
79     virCommandSetOutputBuffer(cmd, &help);
80 
81     if (virCommandRun(cmd, NULL) < 0)
82         goto cleanup;
83 
84     tmp = help;
85 
86     /* expected format: vzctl version <major>.<minor>.<micro> */
87     if ((tmp = STRSKIP(tmp, "vzctl version ")) == NULL)
88         goto cleanup;
89 
90     if (virParseVersionString(tmp, &version, true) < 0)
91         goto cleanup;
92 
93     if (retversion)
94         *retversion = version;
95 
96     ret = 0;
97 
98  cleanup:
99     virCommandFree(cmd);
100     VIR_FREE(help);
101 
102     return ret;
103 }
104 
openvzExtractVersion(struct openvz_driver * driver)105 int openvzExtractVersion(struct openvz_driver *driver)
106 {
107     if (driver->version > 0)
108         return 0;
109 
110     if (openvzExtractVersionInfo(VZCTL, &driver->version) < 0) {
111         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
112                        _("Could not extract vzctl version"));
113         return -1;
114     }
115 
116     return 0;
117 }
118 
119 
120 /* Parse config values of the form barrier:limit into barrier and limit */
121 static int
openvzParseBarrierLimit(const char * value,unsigned long long * barrier,unsigned long long * limit)122 openvzParseBarrierLimit(const char* value,
123                         unsigned long long *barrier,
124                         unsigned long long *limit)
125 {
126     g_auto(GStrv) tmp = NULL;
127 
128     if (!(tmp = g_strsplit(value, ":", 0)))
129         return -1;
130 
131     if (g_strv_length(tmp) != 2)
132         return -1;
133 
134     if (barrier && virStrToLong_ull(tmp[0], NULL, 10, barrier) < 0)
135         return -1;
136 
137     if (limit && virStrToLong_ull(tmp[1], NULL, 10, limit) < 0)
138         return -1;
139 
140     return 0;
141 }
142 
143 
openvzCapsInit(void)144 virCaps *openvzCapsInit(void)
145 {
146     g_autoptr(virCaps) caps = NULL;
147     virCapsGuest *guest;
148 
149     if ((caps = virCapabilitiesNew(virArchFromHost(),
150                                    false, false)) == NULL)
151         return NULL;
152 
153     if (!(caps->host.numa = virCapabilitiesHostNUMANewHost()))
154         return NULL;
155 
156     if (virCapabilitiesInitCaches(caps) < 0)
157         return NULL;
158 
159     guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_EXE,
160                                     caps->host.arch, NULL, NULL, 0, NULL);
161 
162     virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_OPENVZ,
163                                   NULL, NULL, 0, NULL);
164 
165     return g_steal_pointer(&caps);
166 }
167 
168 
169 int
openvzReadNetworkConf(virDomainDef * def,int veid)170 openvzReadNetworkConf(virDomainDef *def,
171                       int veid)
172 {
173     int ret;
174     virDomainNetDef *net = NULL;
175     char *temp = NULL;
176     char *token, *saveptr = NULL;
177 
178     /*parse routing network configuration*
179      * Sample from config:
180      *   IP_ADDRESS="1.1.1.1 1.1.1.2"
181      * IPs split by space
182      */
183     ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
184     if (ret < 0) {
185         virReportError(VIR_ERR_INTERNAL_ERROR,
186                        _("Could not read 'IP_ADDRESS' from config for container %d"),
187                        veid);
188         goto error;
189     } else if (ret > 0) {
190         token = strtok_r(temp, " ", &saveptr);
191         while (token != NULL) {
192             net = g_new0(virDomainNetDef, 1);
193 
194             net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
195             if (virDomainNetAppendIPAddress(net, token, AF_UNSPEC, 0) < 0)
196                 goto error;
197 
198             VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net);
199 
200             token = strtok_r(NULL, " ", &saveptr);
201         }
202     }
203 
204     /*parse bridge devices*/
205     /*Sample from config:
206      * NETIF="ifname=eth10,mac=00:18:51:C1:05:EE,host_ifname=veth105.10,host_mac=00:18:51:8F:D9:F3"
207      *devices split by ';'
208      */
209     ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
210     if (ret < 0) {
211         virReportError(VIR_ERR_INTERNAL_ERROR,
212                        _("Could not read 'NETIF' from config for container %d"),
213                        veid);
214         goto error;
215     } else if (ret > 0) {
216         g_auto(GStrv) devices = g_strsplit(temp, ";", 0);
217         GStrv device;
218 
219         for (device = devices; device && *device; device++) {
220             g_auto(GStrv) keyvals = g_strsplit(*device, ",", 0);
221             GStrv keyval;
222 
223             net = g_new0(virDomainNetDef, 1);
224             net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
225 
226             for (keyval = keyvals; keyval && *keyval; keyval++) {
227                 char *val = strchr(*keyval, '=');
228 
229                 if (!val)
230                     continue;
231 
232                 val++;
233 
234                 if (STRPREFIX(*keyval, "ifname=")) {
235                     /* skip in libvirt */
236                 } else if (STRPREFIX(*keyval, "host_ifname=")) {
237                     if (strlen(val) > 16) {
238                         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
239                                        _("Too long network device name"));
240                         goto error;
241                     }
242 
243                     net->ifname = g_strdup(val);
244                 } else if (STRPREFIX(*keyval, "bridge=")) {
245                     if (strlen(val) > 16) {
246                         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
247                                        _("Too long bridge device name"));
248                         goto error;
249                     }
250 
251                     net->data.bridge.brname = g_strdup(val);
252                 } else if (STRPREFIX(*keyval, "mac=")) {
253                     if (strlen(val) != 17) { /* should be 17 */
254                         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
255                                        _("Wrong length MAC address"));
256                         goto error;
257                     }
258                     if (virMacAddrParse(val, &net->mac) < 0) {
259                         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
260                                        _("Wrong MAC address"));
261                         goto error;
262                     }
263                 }
264             }
265 
266             VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net);
267         }
268     }
269 
270     VIR_FREE(temp);
271 
272     return 0;
273 
274  error:
275     VIR_FREE(temp);
276     virDomainNetDefFree(net);
277     return -1;
278 }
279 
280 
281 static int
openvzReadFSConf(virDomainDef * def,int veid)282 openvzReadFSConf(virDomainDef *def,
283                  int veid)
284 {
285     int ret;
286     virDomainFSDef *fs = NULL;
287     g_autofree char *veid_str = NULL;
288     char *temp = NULL;
289     const char *param;
290     unsigned long long barrier, limit;
291 
292     ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
293     if (ret < 0) {
294         virReportError(VIR_ERR_INTERNAL_ERROR,
295                        _("Could not read 'OSTEMPLATE' from config for container %d"),
296                        veid);
297         goto error;
298     } else if (ret > 0) {
299         if (!(fs = virDomainFSDefNew(NULL)))
300             goto error;
301 
302         fs->type = VIR_DOMAIN_FS_TYPE_TEMPLATE;
303         fs->src->path = g_strdup(temp);
304     } else {
305         /* OSTEMPLATE was not found, VE was booted from a private dir directly */
306         ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
307         if (ret <= 0) {
308             virReportError(VIR_ERR_INTERNAL_ERROR,
309                            _("Could not read 'VE_PRIVATE' from config for container %d"),
310                            veid);
311             goto error;
312         }
313 
314         if (!(fs = virDomainFSDefNew(NULL)))
315             goto error;
316 
317         veid_str = g_strdup_printf("%d", veid);
318 
319         fs->type = VIR_DOMAIN_FS_TYPE_MOUNT;
320         if (!(fs->src->path = virStringReplace(temp, "$VEID", veid_str)))
321             goto error;
322     }
323 
324     fs->dst = g_strdup("/");
325 
326     param = "DISKSPACE";
327     ret = openvzReadVPSConfigParam(veid, param, &temp);
328     if (ret > 0) {
329         if (openvzParseBarrierLimit(temp, &barrier, &limit)) {
330             virReportError(VIR_ERR_INTERNAL_ERROR,
331                            _("Could not read '%s' from config for container %d"),
332                            param, veid);
333             goto error;
334         } else {
335             /* Ensure that we can multiply by 1024 without overflowing. */
336             if (barrier > ULLONG_MAX / 1024 ||
337                 limit > ULLONG_MAX / 1024) {
338                 virReportError(VIR_ERR_OVERFLOW, "%s",
339                                _("Unable to parse quota"));
340                 goto error;
341             }
342             fs->space_soft_limit = barrier * 1024; /* unit is bytes */
343             fs->space_hard_limit = limit * 1024;   /* unit is bytes */
344         }
345     }
346 
347     VIR_APPEND_ELEMENT(def->fss, def->nfss, fs);
348 
349     VIR_FREE(temp);
350 
351     return 0;
352  error:
353     VIR_FREE(temp);
354     virDomainFSDefFree(fs);
355     return -1;
356 }
357 
358 
359 static int
openvzReadMemConf(virDomainDef * def,int veid)360 openvzReadMemConf(virDomainDef *def, int veid)
361 {
362     int ret = -1;
363     char *temp = NULL;
364     unsigned long long barrier, limit;
365     const char *param;
366     long kb_per_pages;
367 
368     kb_per_pages = openvzKBPerPages();
369     if (kb_per_pages < 0)
370         goto error;
371 
372     /* Memory allocation guarantee */
373     param = "VMGUARPAGES";
374     ret = openvzReadVPSConfigParam(veid, param, &temp);
375     if (ret < 0) {
376         virReportError(VIR_ERR_INTERNAL_ERROR,
377                        _("Could not read '%s' from config for container %d"),
378                        param, veid);
379         goto error;
380     } else if (ret > 0) {
381         ret = openvzParseBarrierLimit(temp, &barrier, NULL);
382         if (ret < 0) {
383             virReportError(VIR_ERR_INTERNAL_ERROR,
384                            _("Could not parse barrier of '%s' "
385                              "from config for container %d"), param, veid);
386             goto error;
387         }
388         if (barrier == LONG_MAX)
389             def->mem.min_guarantee = 0ull;
390         else
391             def->mem.min_guarantee = barrier * kb_per_pages;
392     }
393 
394     /* Memory hard and soft limits */
395     param = "PRIVVMPAGES";
396     ret = openvzReadVPSConfigParam(veid, param, &temp);
397     if (ret < 0) {
398         virReportError(VIR_ERR_INTERNAL_ERROR,
399                        _("Could not read '%s' from config for container %d"),
400                        param, veid);
401         goto error;
402     } else if (ret > 0) {
403         ret = openvzParseBarrierLimit(temp, &barrier, &limit);
404         if (ret < 0) {
405             virReportError(VIR_ERR_INTERNAL_ERROR,
406                            _("Could not parse barrier and limit of '%s' "
407                              "from config for container %d"), param, veid);
408             goto error;
409         }
410         if (barrier == LONG_MAX)
411             def->mem.soft_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
412         else
413             def->mem.soft_limit = barrier * kb_per_pages;
414 
415         if (limit == LONG_MAX)
416             def->mem.hard_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
417         else
418             def->mem.hard_limit = limit * kb_per_pages;
419     }
420 
421     ret = 0;
422  error:
423     VIR_FREE(temp);
424     return ret;
425 }
426 
427 
428 /* Free all memory associated with a openvz_driver structure */
429 void
openvzFreeDriver(struct openvz_driver * driver)430 openvzFreeDriver(struct openvz_driver *driver)
431 {
432     if (!driver)
433         return;
434 
435     virObjectUnref(driver->xmlopt);
436     virObjectUnref(driver->domains);
437     virObjectUnref(driver->caps);
438     g_free(driver);
439 }
440 
441 
442 
openvzLoadDomains(struct openvz_driver * driver)443 int openvzLoadDomains(struct openvz_driver *driver)
444 {
445     int veid, ret;
446     char *status;
447     char uuidstr[VIR_UUID_STRING_BUFLEN];
448     virDomainObj *dom = NULL;
449     virDomainDef *def = NULL;
450     char *temp = NULL;
451     char *outbuf = NULL;
452     char *line;
453     virCommand *cmd = NULL;
454     unsigned int vcpus = 0;
455 
456     if (openvzAssignUUIDs() < 0)
457         return -1;
458 
459     cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
460     virCommandSetOutputBuffer(cmd, &outbuf);
461     if (virCommandRun(cmd, NULL) < 0)
462         goto cleanup;
463 
464     line = outbuf;
465     while (line[0] != '\0') {
466         unsigned int flags = 0;
467         if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
468             *status++ != ' ' ||
469             (line = strchr(status, '\n')) == NULL) {
470             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
471                            _("Failed to parse vzlist output"));
472             goto cleanup;
473         }
474         *line++ = '\0';
475 
476         if (!(def = virDomainDefNew(driver->xmlopt)))
477             goto cleanup;
478 
479         def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
480 
481         if (STREQ(status, "stopped"))
482             def->id = -1;
483         else
484             def->id = veid;
485         def->name = g_strdup_printf("%i", veid);
486 
487         openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
488         ret = virUUIDParse(uuidstr, def->uuid);
489 
490         if (ret == -1) {
491             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
492                            _("UUID in config file malformed"));
493             goto cleanup;
494         }
495 
496         def->os.type = VIR_DOMAIN_OSTYPE_EXE;
497         def->os.init = g_strdup("/sbin/init");
498 
499         ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
500         if (ret < 0) {
501             virReportError(VIR_ERR_INTERNAL_ERROR,
502                            _("Could not read config for container %d"),
503                            veid);
504             goto cleanup;
505         } else if (ret > 0) {
506             vcpus = strtoI(temp);
507         }
508 
509         if (ret == 0 || vcpus == 0)
510             vcpus = virHostCPUGetCount();
511 
512         if (virDomainDefSetVcpusMax(def, vcpus, driver->xmlopt) < 0)
513             goto cleanup;
514 
515         if (virDomainDefSetVcpus(def, vcpus) < 0)
516             goto cleanup;
517 
518         /* XXX load rest of VM config data .... */
519 
520         openvzReadNetworkConf(def, veid);
521         openvzReadFSConf(def, veid);
522         openvzReadMemConf(def, veid);
523 
524         virUUIDFormat(def->uuid, uuidstr);
525         flags = VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE;
526         if (STRNEQ(status, "stopped"))
527             flags |= VIR_DOMAIN_OBJ_LIST_ADD_LIVE;
528 
529         if (!(dom = virDomainObjListAdd(driver->domains,
530                                         &def,
531                                         driver->xmlopt,
532                                         flags,
533                                         NULL)))
534             goto cleanup;
535 
536         if (STREQ(status, "stopped")) {
537             virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
538                                  VIR_DOMAIN_SHUTOFF_UNKNOWN);
539             dom->pid = -1;
540         } else {
541             virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
542                                  VIR_DOMAIN_RUNNING_UNKNOWN);
543             dom->pid = veid;
544         }
545         /* XXX OpenVZ doesn't appear to have concept of a transient domain */
546         dom->persistent = 1;
547 
548         virDomainObjEndAPI(&dom);
549         dom = NULL;
550     }
551 
552     virCommandFree(cmd);
553     VIR_FREE(temp);
554     VIR_FREE(outbuf);
555 
556     return 0;
557 
558  cleanup:
559     virCommandFree(cmd);
560     VIR_FREE(temp);
561     VIR_FREE(outbuf);
562     virDomainDefFree(def);
563     return -1;
564 }
565 
566 static int
openvzWriteConfigParam(const char * conf_file,const char * param,const char * value)567 openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
568 {
569     g_autofree char *temp_file = NULL;
570     int temp_fd = -1;
571     FILE *fp;
572     char *line = NULL;
573     size_t line_size = 0;
574 
575     temp_file = g_strdup_printf("%s.tmp", conf_file);
576 
577     fp = fopen(conf_file, "r");
578     if (fp == NULL)
579         goto error;
580     temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
581     if (temp_fd == -1)
582         goto error;
583 
584     while (1) {
585         if (getline(&line, &line_size, fp) <= 0)
586             break;
587 
588         if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
589             if (safewrite(temp_fd, line, strlen(line)) !=
590                 strlen(line))
591                 goto error;
592         }
593     }
594 
595     if (safewrite(temp_fd, param, strlen(param)) < 0 ||
596         safewrite(temp_fd, "=\"", 2) < 0 ||
597         safewrite(temp_fd, value, strlen(value)) < 0 ||
598         safewrite(temp_fd, "\"\n", 2) < 0)
599         goto error;
600 
601     if (VIR_FCLOSE(fp) < 0)
602         goto error;
603     if (VIR_CLOSE(temp_fd) < 0)
604         goto error;
605 
606     if (rename(temp_file, conf_file) < 0)
607         goto error;
608 
609     VIR_FREE(line);
610 
611     return 0;
612 
613  error:
614     VIR_FREE(line);
615     VIR_FORCE_FCLOSE(fp);
616     VIR_FORCE_CLOSE(temp_fd);
617     if (temp_file)
618         unlink(temp_file);
619     return -1;
620 }
621 
622 int
openvzWriteVPSConfigParam(int vpsid,const char * param,const char * value)623 openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
624 {
625     char *conf_file;
626     int ret;
627 
628     if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
629         return -1;
630 
631     ret = openvzWriteConfigParam(conf_file, param, value);
632     VIR_FREE(conf_file);
633     return ret;
634 }
635 
636 /*
637  * value will be freed before a new value is assigned to it, the caller is
638  * responsible for freeing it afterwards.
639  *
640  * Returns <0 on error, 0 if not found, 1 if found.
641  */
642 int
openvzReadConfigParam(const char * conf_file,const char * param,char ** value)643 openvzReadConfigParam(const char *conf_file, const char *param, char **value)
644 {
645     char *line = NULL;
646     size_t line_size = 0;
647     FILE *fp;
648     int err = 0;
649     char *sf, *token, *saveptr = NULL;
650 
651     fp = fopen(conf_file, "r");
652     if (fp == NULL)
653         return -1;
654 
655     VIR_FREE(*value);
656     while (1) {
657         if (getline(&line, &line_size, fp) < 0) {
658             err = !feof(fp);
659             break;
660         }
661 
662         if (!(sf = STRSKIP(line, param)))
663             continue;
664 
665         if (*sf++ != '=')
666             continue;
667 
668         saveptr = NULL;
669         if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
670             VIR_FREE(*value);
671             *value = g_strdup(token);
672             /* keep going - last entry wins */
673         }
674     }
675     VIR_FREE(line);
676     VIR_FORCE_FCLOSE(fp);
677 
678     return err ? -1 : *value ? 1 : 0;
679 }
680 
681 /*
682  * Read parameter from container config
683  *
684  * value will be freed before a new value is assigned to it, the caller is
685  * responsible for freeing it afterwards.
686  *
687  * sample: 133, "OSTEMPLATE", &value
688  * return: -1 - error
689  *          0 - don't found
690  *          1 - OK
691  */
692 int
openvzReadVPSConfigParam(int vpsid,const char * param,char ** value)693 openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
694 {
695     char *conf_file;
696     int ret;
697 
698     if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
699         return -1;
700 
701     ret = openvzReadConfigParam(conf_file, param, value);
702     VIR_FREE(conf_file);
703     return ret;
704 }
705 
706 static int
openvz_copyfile(char * from_path,char * to_path)707 openvz_copyfile(char* from_path, char* to_path)
708 {
709     char *line = NULL;
710     size_t line_size = 0;
711     FILE *fp;
712     int copy_fd;
713     int bytes_read;
714 
715     fp = fopen(from_path, "r");
716     if (fp == NULL)
717         return -1;
718     copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
719     if (copy_fd == -1) {
720         VIR_FORCE_FCLOSE(fp);
721         return -1;
722     }
723 
724     while (1) {
725         if (getline(&line, &line_size, fp) <= 0)
726             break;
727 
728         bytes_read = strlen(line);
729         if (safewrite(copy_fd, line, bytes_read) != bytes_read)
730             goto error;
731     }
732 
733     if (VIR_FCLOSE(fp) < 0)
734         goto error;
735     if (VIR_CLOSE(copy_fd) < 0)
736         goto error;
737 
738     VIR_FREE(line);
739 
740     return 0;
741 
742  error:
743     VIR_FREE(line);
744     VIR_FORCE_FCLOSE(fp);
745     VIR_FORCE_CLOSE(copy_fd);
746     return -1;
747 }
748 
749 /*
750 * Copy the default config to the VE conf file
751 * return: -1 - error
752 *          0 - OK
753 */
754 int
openvzCopyDefaultConfig(int vpsid)755 openvzCopyDefaultConfig(int vpsid)
756 {
757     char *confdir = NULL;
758     char *default_conf_file = NULL;
759     char *configfile_value = NULL;
760     char *conf_file = NULL;
761     int ret = -1;
762 
763     if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
764         goto cleanup;
765 
766     confdir = openvzLocateConfDir();
767     if (confdir == NULL)
768         goto cleanup;
769 
770     default_conf_file = g_strdup_printf("%s/ve-%s.conf-sample", confdir,
771                                         configfile_value);
772 
773     if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
774         goto cleanup;
775 
776     if (openvz_copyfile(default_conf_file, conf_file)<0)
777         goto cleanup;
778 
779     ret = 0;
780  cleanup:
781     VIR_FREE(confdir);
782     VIR_FREE(default_conf_file);
783     VIR_FREE(configfile_value);
784     VIR_FREE(conf_file);
785     return ret;
786 }
787 
788 /* Locate config file of container
789  * return -1 - error
790  *         0 - OK */
791 static int
openvzLocateConfFileDefault(int vpsid,char ** conffile,const char * ext)792 openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
793 {
794     char *confdir;
795     int ret = 0;
796 
797     confdir = openvzLocateConfDir();
798     if (confdir == NULL)
799         return -1;
800 
801     *conffile = g_strdup_printf("%s/%d.%s", confdir, vpsid, ext ? ext : "conf");
802 
803     VIR_FREE(confdir);
804     return ret;
805 }
806 
807 static char *
openvzLocateConfDir(void)808 openvzLocateConfDir(void)
809 {
810     const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
811     size_t i = 0;
812 
813     while (conf_dir_list[i]) {
814         if (virFileExists(conf_dir_list[i]))
815             return g_strdup(conf_dir_list[i]);
816 
817         i++;
818     }
819 
820     return NULL;
821 }
822 
823 /* Richard Steven's classic readline() function */
824 int
openvz_readline(int fd,char * ptr,int maxlen)825 openvz_readline(int fd, char *ptr, int maxlen)
826 {
827     int n, rc;
828     char c;
829 
830     for (n = 1; n < maxlen; n++) {
831         if ((rc = read(fd, &c, 1)) == 1) {
832             *ptr++ = c;
833             if (c == '\n')
834                 break;
835         } else if (rc == 0) {
836             if (n == 1)
837                 return 0; /* EOF condition */
838             else
839                 break;
840         }
841         else
842             return -1; /* error */
843     }
844     *ptr = 0;
845     return n;
846 }
847 
848 static int
openvzGetVPSUUID(int vpsid,char * uuidstr,size_t len)849 openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
850 {
851     char *conf_file;
852     char *line = NULL;
853     size_t line_size = 0;
854     char *saveptr = NULL;
855     char *uuidbuf;
856     char *iden;
857     FILE *fp;
858     int retval = -1;
859 
860     if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
861         return -1;
862 
863     fp = fopen(conf_file, "r");
864     if (fp == NULL)
865         goto cleanup;
866 
867     while (1) {
868         if (getline(&line, &line_size, fp) < 0) {
869             if (feof(fp)) { /* EOF, UUID was not found */
870                 uuidstr[0] = 0;
871                 break;
872             } else {
873                 goto cleanup;
874             }
875         }
876 
877         iden = strtok_r(line, " ", &saveptr);
878         uuidbuf = strtok_r(NULL, "\n", &saveptr);
879 
880         if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
881             if (virStrcpy(uuidstr, uuidbuf, len) < 0) {
882                 virReportError(VIR_ERR_INTERNAL_ERROR,
883                                _("invalid uuid %s"), uuidbuf);
884                 goto cleanup;
885             }
886             break;
887         }
888     }
889     retval = 0;
890  cleanup:
891     VIR_FREE(line);
892     VIR_FORCE_FCLOSE(fp);
893     VIR_FREE(conf_file);
894 
895     return retval;
896 }
897 
898 /* Do actual checking for UUID presence in conf file,
899  * assign if not present.
900  */
901 int
openvzSetDefinedUUID(int vpsid,unsigned char * uuid)902 openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
903 {
904     char *conf_file;
905     char uuidstr[VIR_UUID_STRING_BUFLEN];
906     FILE *fp = NULL;
907     int ret = -1;
908 
909     if (uuid == NULL)
910         return -1;
911 
912     if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
913         return -1;
914 
915     if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
916         goto cleanup;
917 
918     if (uuidstr[0] == 0) {
919         fp = fopen(conf_file, "a"); /* append */
920         if (fp == NULL)
921             goto cleanup;
922 
923         virUUIDFormat(uuid, uuidstr);
924 
925         /* Record failure if fprintf or VIR_FCLOSE fails,
926            and be careful always to close the stream.  */
927         if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
928             (VIR_FCLOSE(fp) == EOF))
929             goto cleanup;
930     }
931 
932     ret = 0;
933  cleanup:
934     VIR_FORCE_FCLOSE(fp);
935     VIR_FREE(conf_file);
936     return ret;
937 }
938 
939 static int
openvzSetUUID(int vpsid)940 openvzSetUUID(int vpsid)
941 {
942     unsigned char uuid[VIR_UUID_BUFLEN];
943 
944     if (virUUIDGenerate(uuid) < 0) {
945         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
946                        _("Failed to generate UUID"));
947         return -1;
948     }
949 
950     return openvzSetDefinedUUID(vpsid, uuid);
951 }
952 
953 /*
954  * Scan VPS config files and see if they have a UUID.
955  * If not, assign one. Just append one to the config
956  * file as comment so that the OpenVZ tools ignore it.
957  *
958  */
959 
openvzAssignUUIDs(void)960 static int openvzAssignUUIDs(void)
961 {
962     g_autoptr(DIR) dp = NULL;
963     struct dirent *dent;
964     char *conf_dir;
965     int vpsid;
966     char *ext;
967     int ret = 0;
968 
969     conf_dir = openvzLocateConfDir();
970     if (conf_dir == NULL)
971         return -1;
972 
973     if (virDirOpenQuiet(&dp, conf_dir) < 0) {
974         VIR_FREE(conf_dir);
975         return 0;
976     }
977 
978     while ((ret = virDirRead(dp, &dent, conf_dir)) > 0) {
979         if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
980             *ext++ != '.' ||
981             STRNEQ(ext, "conf"))
982             continue;
983         if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
984             openvzSetUUID(vpsid);
985     }
986 
987     VIR_FREE(conf_dir);
988     return ret;
989 }
990 
991 
992 /*
993  * Return CTID from name
994  *
995  */
996 
openvzGetVEID(const char * name)997 int openvzGetVEID(const char *name)
998 {
999     virCommand *cmd;
1000     char *outbuf;
1001     char *temp;
1002     int veid;
1003     bool ok;
1004 
1005     cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
1006     virCommandSetOutputBuffer(cmd, &outbuf);
1007     if (virCommandRun(cmd, NULL) < 0) {
1008         virCommandFree(cmd);
1009         VIR_FREE(outbuf);
1010         return -1;
1011     }
1012 
1013     virCommandFree(cmd);
1014     ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
1015     VIR_FREE(outbuf);
1016 
1017     if (ok && veid >= 0)
1018         return veid;
1019 
1020     virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1021                    _("Failed to parse vzlist output"));
1022     return -1;
1023 }
1024 
1025 
1026 static int
openvzDomainDefPostParse(virDomainDef * def,unsigned int parseFlags G_GNUC_UNUSED,void * opaque,void * parseOpaque G_GNUC_UNUSED)1027 openvzDomainDefPostParse(virDomainDef *def,
1028                          unsigned int parseFlags G_GNUC_UNUSED,
1029                          void *opaque,
1030                          void *parseOpaque G_GNUC_UNUSED)
1031 {
1032     struct openvz_driver *driver = opaque;
1033     if (!virCapabilitiesDomainSupported(driver->caps, def->os.type,
1034                                         def->os.arch,
1035                                         def->virtType))
1036         return -1;
1037 
1038     /* fill the init path */
1039     if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init)
1040         def->os.init = g_strdup("/sbin/init");
1041 
1042     return 0;
1043 }
1044 
1045 
1046 static int
openvzDomainDeviceDefPostParse(virDomainDeviceDef * dev,const virDomainDef * def G_GNUC_UNUSED,unsigned int parseFlags G_GNUC_UNUSED,void * opaque G_GNUC_UNUSED,void * parseOpaque G_GNUC_UNUSED)1047 openvzDomainDeviceDefPostParse(virDomainDeviceDef *dev,
1048                                const virDomainDef *def G_GNUC_UNUSED,
1049                                unsigned int parseFlags G_GNUC_UNUSED,
1050                                void *opaque G_GNUC_UNUSED,
1051                                void *parseOpaque G_GNUC_UNUSED)
1052 {
1053     if (dev->type == VIR_DOMAIN_DEVICE_CHR &&
1054         dev->data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
1055         dev->data.chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)
1056         dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ;
1057 
1058     /* forbid capabilities mode hostdev in this kind of hypervisor */
1059     if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
1060         dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
1061         virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
1062                        _("hostdev mode 'capabilities' is not "
1063                          "supported in %s"),
1064                        virDomainVirtTypeToString(def->virtType));
1065         return -1;
1066     }
1067 
1068     if (dev->type == VIR_DOMAIN_DEVICE_VIDEO &&
1069         dev->data.video->type == VIR_DOMAIN_VIDEO_TYPE_DEFAULT) {
1070         if (def->os.type == VIR_DOMAIN_OSTYPE_HVM)
1071             dev->data.video->type = VIR_DOMAIN_VIDEO_TYPE_VGA;
1072         else
1073             dev->data.video->type = VIR_DOMAIN_VIDEO_TYPE_PARALLELS;
1074     }
1075 
1076     return 0;
1077 }
1078 
1079 
1080 virDomainDefParserConfig openvzDomainDefParserConfig = {
1081     .domainPostParseCallback = openvzDomainDefPostParse,
1082     .devicesPostParseCallback = openvzDomainDeviceDefPostParse,
1083     .features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
1084 };
1085 
openvzXMLOption(struct openvz_driver * driver)1086 virDomainXMLOption *openvzXMLOption(struct openvz_driver *driver)
1087 {
1088     openvzDomainDefParserConfig.priv = driver;
1089     return virDomainXMLOptionNew(&openvzDomainDefParserConfig,
1090                                  NULL, NULL, NULL, NULL);
1091 }
1092