openvz_conf.c 29.8 KB
Newer Older
1 2 3
/*
 * openvz_conf.c: config functions for managing OpenVZ VEs
 *
4
 * Copyright (C) 2010-2012, 2014 Red Hat, Inc.
5 6
 * Copyright (C) 2006, 2007 Binary Karma
 * Copyright (C) 2006 Shuveb Hussain
7
 * Copyright (C) 2007 Anoop Joe Cyriac
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
20
 * License along with this library.  If not, see
O
Osier Yang 已提交
21
 * <http://www.gnu.org/licenses/>.
22 23
 */

24
#include <config.h>
J
Jim Meyering 已提交
25

26 27 28 29 30
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#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"
E
Eric Blake 已提交
39
#include "virfile.h"
40
#include "vircommand.h"
41
#include "virstring.h"
42
#include "virhostcpu.h"
43

44 45
#define VIR_FROM_THIS VIR_FROM_OPENVZ

46
static char *openvzLocateConfDir(void);
47
static int openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len);
48
static int openvzAssignUUIDs(void);
49 50 51
static int openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext);

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
52

53
int
54
strtoI(const char *str)
55 56 57
{
    int val;

58
    if (virStrToLong_i(str, NULL, 10, &val) < 0)
E
Eric Blake 已提交
59
        return 0;
60

61 62 63
    return val;
}

64 65

static int
66
openvzExtractVersionInfo(const char *cmdstr, int *retversion)
67
{
68
    int ret = -1;
69
    unsigned long version;
70
    char *help = NULL;
71
    char *tmp;
72
    virCommandPtr cmd = virCommandNewArgList(cmdstr, "--help", NULL);
73 74 75 76

    if (retversion)
        *retversion = 0;

77 78
    virCommandAddEnvString(cmd, "LC_ALL=C");
    virCommandSetOutputBuffer(cmd, &help);
79

80 81
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
82

83 84 85 86
    tmp = help;

    /* expected format: vzctl version <major>.<minor>.<micro> */
    if ((tmp = STRSKIP(tmp, "vzctl version ")) == NULL)
87
        goto cleanup;
88

I
Ilja Livenson 已提交
89
    if (virParseVersionString(tmp, &version, true) < 0)
90
        goto cleanup;
91 92 93 94 95 96

    if (retversion)
        *retversion = version;

    ret = 0;

97
 cleanup:
98
    virCommandFree(cmd);
99 100 101 102 103
    VIR_FREE(help);

    return ret;
}

104
int openvzExtractVersion(struct openvz_driver *driver)
105 106 107 108 109
{
    if (driver->version > 0)
        return 0;

    if (openvzExtractVersionInfo(VZCTL, &driver->version) < 0) {
110 111
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Could not extract vzctl version"));
112 113 114 115 116 117 118
        return -1;
    }

    return 0;
}


119 120 121 122 123 124
/* Parse config values of the form barrier:limit into barrier and limit */
static int
openvzParseBarrierLimit(const char* value,
                        unsigned long long *barrier,
                        unsigned long long *limit)
{
125 126
    char **tmp = NULL;
    size_t ntmp = 0;
P
Pavel Hrdina 已提交
127
    int ret = -1;
128

129
    if (!(tmp = virStringSplitCount(value, ":", 0, &ntmp)))
130 131
        goto error;

132
    if (ntmp != 2)
133
        goto error;
134 135

    if (barrier && virStrToLong_ull(tmp[0], NULL, 10, barrier) < 0)
136
        goto error;
137 138 139 140

    if (limit && virStrToLong_ull(tmp[1], NULL, 10, limit) < 0)
        goto error;

P
Pavel Hrdina 已提交
141
    ret = 0;
142
 error:
143
    virStringListFreeCount(tmp, ntmp);
P
Pavel Hrdina 已提交
144
    return ret;
145 146 147
}


148 149
virCapsPtr openvzCapsInit(void)
{
150
    g_autoptr(virCaps) caps = NULL;
151 152
    virCapsGuestPtr guest;

153
    if ((caps = virCapabilitiesNew(virArchFromHost(),
154
                                   false, false)) == NULL)
155
        return NULL;
156

157
    if (!(caps->host.numa = virCapabilitiesHostNUMANewHost()))
158
        return NULL;
159

160
    if (virCapabilitiesInitCaches(caps) < 0)
161
        return NULL;
162

163
    if ((guest = virCapabilitiesAddGuest(caps,
164
                                         VIR_DOMAIN_OSTYPE_EXE,
165
                                         caps->host.arch,
166 167 168 169
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
170
        return NULL;
171 172

    if (virCapabilitiesAddGuestDomain(guest,
173
                                      VIR_DOMAIN_VIRT_OPENVZ,
174 175 176 177
                                      NULL,
                                      NULL,
                                      0,
                                      NULL) == NULL)
178
        return NULL;
179

180
    return g_steal_pointer(&caps);
181 182 183
}


184
int
185
openvzReadNetworkConf(virDomainDefPtr def,
186 187
                      int veid)
{
188
    int ret;
189
    virDomainNetDefPtr net = NULL;
190
    char *temp = NULL;
191 192 193 194 195
    char *token, *saveptr = NULL;

    /*parse routing network configuration*
     * Sample from config:
     *   IP_ADDRESS="1.1.1.1 1.1.1.2"
N
Nehal J Wani 已提交
196
     * IPs split by space
197
     */
198
    ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
199
    if (ret < 0) {
200 201 202
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'IP_ADDRESS' from config for container %d"),
                       veid);
203 204 205 206
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, " ", &saveptr);
        while (token != NULL) {
207
            if (VIR_ALLOC(net) < 0)
208
                goto error;
209 210

            net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
211
            if (virDomainNetAppendIPAddress(net, token, AF_UNSPEC, 0) < 0)
212
                goto error;
213

214
            if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0)
215
                goto error;
216

217 218 219 220 221 222
            token = strtok_r(NULL, " ", &saveptr);
        }
    }

    /*parse bridge devices*/
    /*Sample from config:
N
Nehal J Wani 已提交
223 224
     * NETIF="ifname=eth10,mac=00:18:51:C1:05:EE,host_ifname=veth105.10,host_mac=00:18:51:8F:D9:F3"
     *devices split by ';'
225
     */
226
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
227
    if (ret < 0) {
228 229 230
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'NETIF' from config for container %d"),
                       veid);
231 232 233 234
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
J
Ján Tomko 已提交
235
            /* add new device to list */
236
            if (VIR_ALLOC(net) < 0)
237
                goto error;
238 239 240

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

241
            char *p = token;
242 243 244
            char cpy_temp[32];
            int len;

J
Ján Tomko 已提交
245
            /* parse string */
246
            do {
247 248 249
                char *next = strchr(p, ',');
                if (!next)
                    next = strchr(p, '\0');
250
                if (STRPREFIX(p, "ifname=")) {
251 252 253
                    /* skip in libvirt */
                } else if (STRPREFIX(p, "host_ifname=")) {
                    p += 12;
254 255
                    len = next - p;
                    if (len > 16) {
256 257
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long network device name"));
258 259 260
                        goto error;
                    }

261
                    if (VIR_ALLOC_N(net->ifname, len+1) < 0)
262
                        goto error;
263

264
                    if (virStrncpy(net->ifname, p, len, len+1) < 0) {
265 266
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Network ifname %s too long for destination"), p);
C
Chris Lalancette 已提交
267 268
                        goto error;
                    }
269 270 271 272
                } else if (STRPREFIX(p, "bridge=")) {
                    p += 7;
                    len = next - p;
                    if (len > 16) {
273 274
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long bridge device name"));
275 276 277
                        goto error;
                    }

278
                    if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
279
                        goto error;
280

281
                    if (virStrncpy(net->data.bridge.brname, p, len, len+1) < 0) {
282 283
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Bridge name %s too long for destination"), p);
C
Chris Lalancette 已提交
284 285
                        goto error;
                    }
286 287 288
                } else if (STRPREFIX(p, "mac=")) {
                    p += 4;
                    len = next - p;
289
                    if (len != 17) { /* should be 17 */
290 291
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong length MAC address"));
292 293
                        goto error;
                    }
294
                    if (virStrncpy(cpy_temp, p, len, sizeof(cpy_temp)) < 0) {
295 296
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("MAC address %s too long for destination"), p);
C
Chris Lalancette 已提交
297 298
                        goto error;
                    }
299
                    if (virMacAddrParse(cpy_temp, &net->mac) < 0) {
300 301
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong MAC address"));
302 303 304 305 306 307
                        goto error;
                    }
                }
                p = ++next;
            } while (p < token + strlen(token));

308
            if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0)
309
                goto error;
310

311 312 313 314
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

315 316
    VIR_FREE(temp);

317
    return 0;
318

319
 error:
320
    VIR_FREE(temp);
321
    virDomainNetDefFree(net);
322
    return -1;
323 324 325
}


326
static int
327
openvzReadFSConf(virDomainDefPtr def,
328 329
                 int veid)
{
330 331
    int ret;
    virDomainFSDefPtr fs = NULL;
332 333
    char *veid_str = NULL;
    char *temp = NULL;
334 335
    const char *param;
    unsigned long long barrier, limit;
336

337
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
338
    if (ret < 0) {
339 340 341
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'OSTEMPLATE' from config for container %d"),
                       veid);
342 343
        goto error;
    } else if (ret > 0) {
344
        if (!(fs = virDomainFSDefNew(NULL)))
345
            goto error;
346 347

        fs->type = VIR_DOMAIN_FS_TYPE_TEMPLATE;
348
        fs->src->path = g_strdup(temp);
349 350
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
351
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
352
        if (ret <= 0) {
353 354 355
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read 'VE_PRIVATE' from config for container %d"),
                           veid);
356 357
            goto error;
        }
358

359
        if (VIR_ALLOC(fs) < 0)
360
            goto error;
361

362
        veid_str = g_strdup_printf("%d", veid);
363 364

        fs->type = VIR_DOMAIN_FS_TYPE_MOUNT;
365
        if (!(fs->src->path = virStringReplace(temp, "$VEID", veid_str)))
366
            goto error;
367

368
        VIR_FREE(veid_str);
369 370
    }

371
    fs->dst = g_strdup("/");
372

373 374 375 376
    param = "DISKSPACE";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret > 0) {
        if (openvzParseBarrierLimit(temp, &barrier, &limit)) {
377 378 379
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read '%s' from config for container %d"),
                           param, veid);
380 381 382
            goto error;
        } else {
            /* Ensure that we can multiply by 1024 without overflowing. */
383
            if (barrier > ULLONG_MAX / 1024 ||
384
                limit > ULLONG_MAX / 1024) {
J
Jiri Denemark 已提交
385 386
                virReportError(VIR_ERR_OVERFLOW, "%s",
                               _("Unable to parse quota"));
387 388 389 390 391 392 393
                goto error;
            }
            fs->space_soft_limit = barrier * 1024; /* unit is bytes */
            fs->space_hard_limit = limit * 1024;   /* unit is bytes */
        }
    }

394
    if (VIR_APPEND_ELEMENT(def->fss, def->nfss, fs) < 0)
395
        goto error;
396

397 398
    VIR_FREE(temp);

399
    return 0;
400
 error:
401
    VIR_FREE(temp);
402 403 404 405 406
    virDomainFSDefFree(fs);
    return -1;
}


407 408 409
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
410
    int ret = -1;
411 412 413
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
414
    long kb_per_pages;
415

416 417
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
418 419 420 421 422 423
        goto error;

    /* Memory allocation guarantee */
    param = "VMGUARPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
424 425 426
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
427 428 429 430
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
        if (ret < 0) {
431 432 433
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier of '%s' "
                             "from config for container %d"), param, veid);
434 435 436 437 438 439 440 441 442 443 444 445
            goto error;
        }
        if (barrier == LONG_MAX)
            def->mem.min_guarantee = 0ull;
        else
            def->mem.min_guarantee = barrier * kb_per_pages;
    }

    /* Memory hard and soft limits */
    param = "PRIVVMPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
446 447 448
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
449 450 451 452
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
        if (ret < 0) {
453 454 455
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier and limit of '%s' "
                             "from config for container %d"), param, veid);
456 457 458
            goto error;
        }
        if (barrier == LONG_MAX)
459
            def->mem.soft_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
460 461 462 463
        else
            def->mem.soft_limit = barrier * kb_per_pages;

        if (limit == LONG_MAX)
464
            def->mem.hard_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
465 466 467 468 469
        else
            def->mem.hard_limit = limit * kb_per_pages;
    }

    ret = 0;
470
 error:
471 472 473 474 475
    VIR_FREE(temp);
    return ret;
}


476 477 478 479 480 481
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
482

483
    virObjectUnref(driver->xmlopt);
484
    virObjectUnref(driver->domains);
485
    virObjectUnref(driver->caps);
486
    VIR_FREE(driver);
487
}
D
Daniel Veillard 已提交
488 489 490



491 492
int openvzLoadDomains(struct openvz_driver *driver)
{
493
    int veid, ret;
494
    char *status;
495
    char uuidstr[VIR_UUID_STRING_BUFLEN];
496
    virDomainObjPtr dom = NULL;
497
    virDomainDefPtr def = NULL;
498
    char *temp = NULL;
E
Eric Blake 已提交
499 500 501
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
502
    unsigned int vcpus = 0;
503

504 505
    if (openvzAssignUUIDs() < 0)
        return -1;
506

E
Eric Blake 已提交
507 508 509 510
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
511

512 513
    line = outbuf;
    while (line[0] != '\0') {
514
        unsigned int flags = 0;
515 516 517
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
518 519
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Failed to parse vzlist output"));
520
            goto cleanup;
521
        }
522
        *line++ = '\0';
523

524
        if (!(def = virDomainDefNew()))
525
            goto cleanup;
526

527
        def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
528

529 530
        if (STREQ(status, "stopped"))
            def->id = -1;
J
Jiri Denemark 已提交
531
        else
532
            def->id = veid;
533
        def->name = g_strdup_printf("%i", veid);
534

535
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
536
        ret = virUUIDParse(uuidstr, def->uuid);
537

538
        if (ret == -1) {
539 540
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
541
            goto cleanup;
542
        }
543

544
        def->os.type = VIR_DOMAIN_OSTYPE_EXE;
545
        def->os.init = g_strdup("/sbin/init");
546

547
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
548
        if (ret < 0) {
549 550 551
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
552
            goto cleanup;
553
        } else if (ret > 0) {
554
            vcpus = strtoI(temp);
555 556
        }

557
        if (ret == 0 || vcpus == 0)
558
            vcpus = virHostCPUGetCount();
559

560
        if (virDomainDefSetVcpusMax(def, vcpus, driver->xmlopt) < 0)
561 562
            goto cleanup;

563 564
        if (virDomainDefSetVcpus(def, vcpus) < 0)
            goto cleanup;
565

566
        /* XXX load rest of VM config data .... */
567

568 569 570
        openvzReadNetworkConf(def, veid);
        openvzReadFSConf(def, veid);
        openvzReadMemConf(def, veid);
571

572
        virUUIDFormat(def->uuid, uuidstr);
573 574 575 576
        flags = VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE;
        if (STRNEQ(status, "stopped"))
            flags |= VIR_DOMAIN_OBJ_LIST_ADD_LIVE;

577 578
        if (!(dom = virDomainObjListAdd(driver->domains,
                                        def,
579
                                        driver->xmlopt,
580 581
                                        flags,
                                        NULL)))
582
            goto cleanup;
583 584 585 586 587 588 589 590 591

        if (STREQ(status, "stopped")) {
            virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
                                 VIR_DOMAIN_SHUTOFF_UNKNOWN);
            dom->pid = -1;
        } else {
            virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
                                 VIR_DOMAIN_RUNNING_UNKNOWN);
            dom->pid = veid;
592
        }
593 594
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
595

596
        virDomainObjEndAPI(&dom);
597
        dom = NULL;
598
        def = NULL;
599
    }
600

E
Eric Blake 已提交
601
    virCommandFree(cmd);
602
    VIR_FREE(temp);
E
Eric Blake 已提交
603
    VIR_FREE(outbuf);
604

605
    return 0;
606

607
 cleanup:
E
Eric Blake 已提交
608
    virCommandFree(cmd);
609
    VIR_FREE(temp);
E
Eric Blake 已提交
610
    VIR_FREE(outbuf);
611
    virDomainDefFree(def);
612
    return -1;
613 614
}

615 616
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
617
{
618
    char * temp_file = NULL;
619 620 621 622
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
623

624
    temp_file = g_strdup_printf("%s.tmp", conf_file);
625

626 627
    fp = fopen(conf_file, "r");
    if (fp == NULL)
628
        goto error;
629
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
630
    if (temp_fd == -1)
631
        goto error;
632

E
Eric Blake 已提交
633
    while (1) {
634
        if (getline(&line, &line_size, fp) <= 0)
635 636
            break;

637
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
638 639 640 641 642 643 644 645 646 647 648 649
            if (safewrite(temp_fd, line, strlen(line)) !=
                strlen(line))
                goto error;
        }
    }

    if (safewrite(temp_fd, param, strlen(param)) < 0 ||
        safewrite(temp_fd, "=\"", 2) < 0 ||
        safewrite(temp_fd, value, strlen(value)) < 0 ||
        safewrite(temp_fd, "\"\n", 2) < 0)
        goto error;

650
    if (VIR_FCLOSE(fp) < 0)
651
        goto error;
652
    if (VIR_CLOSE(temp_fd) < 0)
653 654 655 656 657
        goto error;

    if (rename(temp_file, conf_file) < 0)
        goto error;

658 659
    VIR_FREE(line);

660 661
    return 0;

662
 error:
663 664
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
665
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
666
    if (temp_file)
667 668
        unlink(temp_file);
    VIR_FREE(temp_file);
669 670 671
    return -1;
}

672
int
673 674
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
675 676
    char *conf_file;
    int ret;
677

678
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
679 680
        return -1;

681 682 683
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
684 685
}

686 687 688
/*
 * value will be freed before a new value is assigned to it, the caller is
 * responsible for freeing it afterwards.
689 690
 *
 * Returns <0 on error, 0 if not found, 1 if found.
691
 */
692
int
693
openvzReadConfigParam(const char *conf_file, const char *param, char **value)
694
{
695 696 697
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
698 699
    int err = 0;
    char *sf, *token, *saveptr = NULL;
700

701 702
    fp = fopen(conf_file, "r");
    if (fp == NULL)
703 704
        return -1;

705
    VIR_FREE(*value);
706 707 708 709 710 711
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

J
Ján Tomko 已提交
712
        if (!(sf = STRSKIP(line, param)))
713 714
            continue;

J
Ján Tomko 已提交
715 716
        if (*sf++ != '=')
            continue;
717

718
        saveptr = NULL;
719 720
        if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
            VIR_FREE(*value);
721
            *value = g_strdup(token);
722 723
            /* keep going - last entry wins */
        }
724
    }
725 726
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
727

728
    return err ? -1 : *value ? 1 : 0;
729 730
}

731
/*
732 733
 * Read parameter from container config
 *
N
Nitesh Konkar 已提交
734
 * value will be freed before a new value is assigned to it, the caller is
735 736 737 738 739 740 741
 * responsible for freeing it afterwards.
 *
 * sample: 133, "OSTEMPLATE", &value
 * return: -1 - error
 *          0 - don't found
 *          1 - OK
 */
742
int
743
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
744
{
745 746
    char *conf_file;
    int ret;
747

748
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
749 750
        return -1;

751
    ret = openvzReadConfigParam(conf_file, param, value);
752 753
    VIR_FREE(conf_file);
    return ret;
754 755 756 757 758
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
759 760 761 762
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
763 764
    int bytes_read;

765 766
    fp = fopen(from_path, "r");
    if (fp == NULL)
767 768 769
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
770
        VIR_FORCE_FCLOSE(fp);
771 772 773
        return -1;
    }

E
Eric Blake 已提交
774
    while (1) {
775
        if (getline(&line, &line_size, fp) <= 0)
776 777 778 779 780 781 782
            break;

        bytes_read = strlen(line);
        if (safewrite(copy_fd, line, bytes_read) != bytes_read)
            goto error;
    }

783
    if (VIR_FCLOSE(fp) < 0)
784
        goto error;
785
    if (VIR_CLOSE(copy_fd) < 0)
786 787
        goto error;

788 789
    VIR_FREE(line);

790 791
    return 0;

792
 error:
793 794
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
795
    VIR_FORCE_CLOSE(copy_fd);
796 797 798 799 800 801 802 803 804 805 806
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
807 808 809
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
810
    char *conf_file = NULL;
811 812
    int ret = -1;

813
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
814 815 816 817 818 819
        goto cleanup;

    confdir = openvzLocateConfDir();
    if (confdir == NULL)
        goto cleanup;

820 821
    default_conf_file = g_strdup_printf("%s/ve-%s.conf-sample", confdir,
                                        configfile_value);
822

823
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
824 825 826 827 828 829
        goto cleanup;

    if (openvz_copyfile(default_conf_file, conf_file)<0)
        goto cleanup;

    ret = 0;
830
 cleanup:
831 832
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
833
    VIR_FREE(configfile_value);
834
    VIR_FREE(conf_file);
835 836 837
    return ret;
}

838
/* Locate config file of container
839 840
 * return -1 - error
 *         0 - OK */
841
static int
842
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
843
{
844
    char *confdir;
845 846 847 848 849 850
    int ret = 0;

    confdir = openvzLocateConfDir();
    if (confdir == NULL)
        return -1;

851
    *conffile = g_strdup_printf("%s/%d.%s", confdir, vpsid, ext ? ext : "conf");
852 853 854 855 856

    VIR_FREE(confdir);
    return ret;
}

857 858
static char *
openvzLocateConfDir(void)
859 860
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
861
    size_t i = 0;
862

E
Eric Blake 已提交
863
    while (conf_dir_list[i]) {
864 865 866
        if (virFileExists(conf_dir_list[i]))
            return g_strdup(conf_dir_list[i]);

E
Eric Blake 已提交
867
        i++;
868 869
    }

870
    return NULL;
871 872 873
}

/* Richard Steven's classic readline() function */
874
int
875
openvz_readline(int fd, char *ptr, int maxlen)
876 877 878 879
{
    int n, rc;
    char c;

E
Eric Blake 已提交
880
    for (n = 1; n < maxlen; n++) {
881
        if ((rc = read(fd, &c, 1)) == 1) {
882
            *ptr++ = c;
E
Eric Blake 已提交
883
            if (c == '\n')
884
                break;
E
Eric Blake 已提交
885 886
        } else if (rc == 0) {
            if (n == 1)
887 888 889 890 891 892 893 894 895 896 897
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

898
static int
899
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
900
{
901
    char *conf_file;
902 903
    char *line = NULL;
    size_t line_size = 0;
904
    char *saveptr = NULL;
905 906
    char *uuidbuf;
    char *iden;
907
    FILE *fp;
908
    int retval = -1;
909

910
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
911
        return -1;
912

913 914
    fp = fopen(conf_file, "r");
    if (fp == NULL)
915
        goto cleanup;
916

E
Eric Blake 已提交
917
    while (1) {
918 919 920 921 922 923 924
        if (getline(&line, &line_size, fp) < 0) {
            if (feof(fp)) { /* EOF, UUID was not found */
                uuidstr[0] = 0;
                break;
            } else {
                goto cleanup;
            }
925 926
        }

927 928 929 930
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
931
            if (virStrcpy(uuidstr, uuidbuf, len) < 0) {
932 933
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("invalid uuid %s"), uuidbuf);
934 935
                goto cleanup;
            }
936 937 938
            break;
        }
    }
939
    retval = 0;
940
 cleanup:
941 942
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
943
    VIR_FREE(conf_file);
944

C
Chris Lalancette 已提交
945
    return retval;
946 947 948 949 950
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
951 952
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
953
{
954
    char *conf_file;
955
    char uuidstr[VIR_UUID_STRING_BUFLEN];
956
    FILE *fp = NULL;
957
    int ret = -1;
958 959 960

    if (uuid == NULL)
        return -1;
961

962
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
963
        return -1;
964

965
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
966
        goto cleanup;
967

J
Jim Meyering 已提交
968
    if (uuidstr[0] == 0) {
969
        fp = fopen(conf_file, "a"); /* append */
970
        if (fp == NULL)
971
            goto cleanup;
972

973 974
        virUUIDFormat(uuid, uuidstr);

975
        /* Record failure if fprintf or VIR_FCLOSE fails,
976
           and be careful always to close the stream.  */
977 978
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
            (VIR_FCLOSE(fp) == EOF))
979
            goto cleanup;
980 981
    }

982
    ret = 0;
983
 cleanup:
984
    VIR_FORCE_FCLOSE(fp);
985 986
    VIR_FREE(conf_file);
    return ret;
987 988
}

989
static int
990 991
openvzSetUUID(int vpsid)
{
992 993
    unsigned char uuid[VIR_UUID_BUFLEN];

994
    if (virUUIDGenerate(uuid) < 0) {
995 996
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
997 998
        return -1;
    }
999 1000 1001 1002

    return openvzSetDefinedUUID(vpsid, uuid);
}

1003 1004 1005 1006 1007 1008 1009
/*
 * Scan VPS config files and see if they have a UUID.
 * If not, assign one. Just append one to the config
 * file as comment so that the OpenVZ tools ignore it.
 *
 */

1010
static int openvzAssignUUIDs(void)
1011 1012 1013 1014
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1015 1016 1017
    int vpsid;
    char *ext;
    int ret = 0;
1018 1019

    conf_dir = openvzLocateConfDir();
1020 1021
    if (conf_dir == NULL)
        return -1;
1022

J
Ján Tomko 已提交
1023
    if (virDirOpenQuiet(&dp, conf_dir) < 0) {
1024
        VIR_FREE(conf_dir);
1025 1026 1027
        return 0;
    }

E
Eric Blake 已提交
1028
    while ((ret = virDirRead(dp, &dent, conf_dir)) > 0) {
1029 1030 1031
        if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
            *ext++ != '.' ||
            STRNEQ(ext, "conf"))
1032
            continue;
E
Eric Blake 已提交
1033
        if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
1034 1035
            openvzSetUUID(vpsid);
    }
1036

J
Ján Tomko 已提交
1037
    VIR_DIR_CLOSE(dp);
1038
    VIR_FREE(conf_dir);
1039
    return ret;
1040
}
1041 1042 1043 1044 1045 1046 1047


/*
 * Return CTID from name
 *
 */

1048 1049
int openvzGetVEID(const char *name)
{
E
Eric Blake 已提交
1050 1051
    virCommandPtr cmd;
    char *outbuf;
1052
    char *temp;
1053
    int veid;
1054
    bool ok;
1055

E
Eric Blake 已提交
1056 1057 1058 1059 1060
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1061 1062 1063
        return -1;
    }

E
Eric Blake 已提交
1064
    virCommandFree(cmd);
1065
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1066
    VIR_FREE(outbuf);
1067

1068 1069 1070
    if (ok && veid >= 0)
        return veid;

1071 1072
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1073 1074
    return -1;
}
1075 1076 1077 1078 1079


static int
openvzDomainDefPostParse(virDomainDefPtr def,
                         unsigned int parseFlags G_GNUC_UNUSED,
1080
                         void *opaque,
1081 1082
                         void *parseOpaque G_GNUC_UNUSED)
{
1083 1084
    struct openvz_driver *driver = opaque;
    if (!virCapabilitiesDomainSupported(driver->caps, def->os.type,
1085 1086 1087 1088
                                        def->os.arch,
                                        def->virtType))
        return -1;

1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
    /* fill the init path */
    if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init)
        def->os.init = g_strdup("/sbin/init");

    return 0;
}


static int
openvzDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
                               const virDomainDef *def G_GNUC_UNUSED,
                               unsigned int parseFlags G_GNUC_UNUSED,
                               void *opaque G_GNUC_UNUSED,
                               void *parseOpaque G_GNUC_UNUSED)
{
    if (dev->type == VIR_DOMAIN_DEVICE_CHR &&
        dev->data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
        dev->data.chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)
        dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ;

    /* forbid capabilities mode hostdev in this kind of hypervisor */
    if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
        dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("hostdev mode 'capabilities' is not "
                         "supported in %s"),
                       virDomainVirtTypeToString(def->virtType));
        return -1;
    }

    return 0;
}


virDomainDefParserConfig openvzDomainDefParserConfig = {
    .domainPostParseCallback = openvzDomainDefPostParse,
    .devicesPostParseCallback = openvzDomainDeviceDefPostParse,
    .features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
};

1129
virDomainXMLOptionPtr openvzXMLOption(struct openvz_driver *driver)
1130
{
1131
    openvzDomainDefParserConfig.priv = driver;
1132 1133 1134
    return virDomainXMLOptionNew(&openvzDomainDefParserConfig,
                                 NULL, NULL, NULL, NULL);
}