openvz_conf.c 30.6 KB
Newer Older
1 2 3
/*
 * openvz_conf.c: config functions for managing OpenVZ VEs
 *
I
Ilja Livenson 已提交
4
 * Copyright (C) 2010-2012 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
O
Osier Yang 已提交
20 21
 * License along with this library;  If not, see
 * <http://www.gnu.org/licenses/>.
22
 *
23
 * Authors:
24 25 26
 * Shuveb Hussain <shuveb@binarykarma.com>
 * Anoop Joe Cyriac <anoop@binarykarma.com>
 *
27 28
 */

29
#include <config.h>
J
Jim Meyering 已提交
30

31 32 33 34 35 36 37
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
38 39 40 41
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
D
Daniel P. Berrange 已提交
42
#include <string.h>
43
#include <sys/utsname.h>
44
#include <sys/wait.h>
45

46
#include "virterror_internal.h"
47
#include "openvz_conf.h"
48
#include "openvz_util.h"
49 50
#include "uuid.h"
#include "buf.h"
51
#include "memory.h"
52
#include "util.h"
53
#include "nodeinfo.h"
E
Eric Blake 已提交
54
#include "virfile.h"
E
Eric Blake 已提交
55
#include "command.h"
56

57 58
#define VIR_FROM_THIS VIR_FROM_OPENVZ

59
static char *openvzLocateConfDir(void);
60
static int openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len);
61
static int openvzAssignUUIDs(void);
62 63 64
static int openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext);

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
65

66
int
67
strtoI(const char *str)
68 69 70
{
    int val;

71
    if (virStrToLong_i(str, NULL, 10, &val) < 0)
E
Eric Blake 已提交
72
        return 0;
73

74 75 76
    return val;
}

77 78

static int
79
openvzExtractVersionInfo(const char *cmdstr, int *retversion)
80
{
81
    int ret = -1;
82
    unsigned long version;
83
    char *help = NULL;
84
    char *tmp;
85
    virCommandPtr cmd = virCommandNewArgList(cmdstr, "--help", NULL);
86 87 88 89

    if (retversion)
        *retversion = 0;

90 91
    virCommandAddEnvString(cmd, "LC_ALL=C");
    virCommandSetOutputBuffer(cmd, &help);
92

93 94
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
95

96 97 98 99
    tmp = help;

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

I
Ilja Livenson 已提交
102
    if (virParseVersionString(tmp, &version, true) < 0)
103
        goto cleanup;
104 105 106 107 108 109

    if (retversion)
        *retversion = version;

    ret = 0;

110 111
cleanup:
    virCommandFree(cmd);
112 113 114 115 116
    VIR_FREE(help);

    return ret;
}

117
int openvzExtractVersion(struct openvz_driver *driver)
118 119 120 121 122
{
    if (driver->version > 0)
        return 0;

    if (openvzExtractVersionInfo(VZCTL, &driver->version) < 0) {
123 124
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Could not extract vzctl version"));
125 126 127 128 129 130 131
        return -1;
    }

    return 0;
}


132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/* 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)
{
    char *token;
    char *saveptr = NULL;
    char *str = strdup(value);

    if (str == NULL) {
        virReportOOMError();
        goto error;
    }

    token = strtok_r(str, ":", &saveptr);
    if (token == NULL) {
        goto error;
    } else {
        if (barrier != NULL) {
            if (virStrToLong_ull(token, NULL, 10, barrier))
                goto error;
        }
    }
    token = strtok_r(NULL, ":", &saveptr);
    if (token == NULL) {
        goto error;
    } else {
        if (limit != NULL) {
            if (virStrToLong_ull(token, NULL, 10, limit))
                goto error;
        }
    }
    return 0;
error:
    VIR_FREE(str);
    return -1;
}


172 173 174 175 176
static int openvzDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED)
{
    return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ;
}

177 178 179 180 181 182 183 184 185 186 187 188
virCapsPtr openvzCapsInit(void)
{
    struct utsname utsname;
    virCapsPtr caps;
    virCapsGuestPtr guest;

    uname(&utsname);

    if ((caps = virCapabilitiesNew(utsname.machine,
                                   0, 0)) == NULL)
        goto no_memory;

189
    if (nodeCapsInitNUMA(caps) < 0)
190 191
        goto no_memory;

192 193
    virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x52, 0x54, 0x00 });

194 195 196
    if ((guest = virCapabilitiesAddGuest(caps,
                                         "exe",
                                         utsname.machine,
197
                                         sizeof(void*) == 4 ? 32 : 64,
198 199 200 201 202 203 204 205 206 207 208 209 210 211
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
        goto no_memory;

    if (virCapabilitiesAddGuestDomain(guest,
                                      "openvz",
                                      NULL,
                                      NULL,
                                      0,
                                      NULL) == NULL)
        goto no_memory;

212
    caps->defaultInitPath = "/sbin/init";
213
    caps->defaultConsoleTargetType = openvzDefaultConsoleType;
214 215

    return caps;
216 217 218 219 220 221
no_memory:
    virCapabilitiesFree(caps);
    return NULL;
}


222
int
223
openvzReadNetworkConf(virDomainDefPtr def,
224
                      int veid) {
225
    int ret;
226
    virDomainNetDefPtr net = NULL;
227
    char *temp = NULL;
228 229 230 231 232 233 234
    char *token, *saveptr = NULL;

    /*parse routing network configuration*
     * Sample from config:
     *   IP_ADDRESS="1.1.1.1 1.1.1.2"
     *   splited IPs by space
     */
235
    ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
236
    if (ret < 0) {
237 238 239
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'IP_ADDRESS' from config for container %d"),
                       veid);
240 241 242 243
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, " ", &saveptr);
        while (token != NULL) {
244
            if (VIR_ALLOC(net) < 0)
245 246 247 248 249 250 251 252
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
            net->data.ethernet.ipaddr = strdup(token);

            if (net->data.ethernet.ipaddr == NULL)
                goto no_memory;

253 254 255 256 257
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

258 259 260 261 262 263 264 265 266
            token = strtok_r(NULL, " ", &saveptr);
        }
    }

    /*parse bridge devices*/
    /*Sample from config:
     *NETIF="ifname=eth10,mac=00:18:51:C1:05:EE,host_ifname=veth105.10,host_mac=00:18:51:8F:D9:F3"
     *devices splited by ';'
     */
267
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
268
    if (ret < 0) {
269 270 271
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'NETIF' from config for container %d"),
                       veid);
272 273 274 275 276
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
            /*add new device to list*/
277
            if (VIR_ALLOC(net) < 0)
278 279 280 281
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

282
            char *p = token;
283 284 285 286 287
            char cpy_temp[32];
            int len;

            /*parse string*/
            do {
288
                char *next = strchrnul (p, ',');
289
                if (STRPREFIX(p, "ifname=")) {
290 291 292
                    /* skip in libvirt */
                } else if (STRPREFIX(p, "host_ifname=")) {
                    p += 12;
293 294
                    len = next - p;
                    if (len > 16) {
295 296
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long network device name"));
297 298 299
                        goto error;
                    }

300 301 302
                    if (VIR_ALLOC_N(net->ifname, len+1) < 0)
                        goto no_memory;

C
Chris Lalancette 已提交
303
                    if (virStrncpy(net->ifname, p, len, len+1) == NULL) {
304 305
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Network ifname %s too long for destination"), p);
C
Chris Lalancette 已提交
306 307
                        goto error;
                    }
308 309 310 311
                } else if (STRPREFIX(p, "bridge=")) {
                    p += 7;
                    len = next - p;
                    if (len > 16) {
312 313
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long bridge device name"));
314 315 316
                        goto error;
                    }

317 318 319
                    if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
                        goto no_memory;

C
Chris Lalancette 已提交
320
                    if (virStrncpy(net->data.bridge.brname, p, len, len+1) == NULL) {
321 322
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Bridge name %s too long for destination"), p);
C
Chris Lalancette 已提交
323 324
                        goto error;
                    }
325 326 327
                } else if (STRPREFIX(p, "mac=")) {
                    p += 4;
                    len = next - p;
328
                    if (len != 17) { /* should be 17 */
329 330
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong length MAC address"));
331 332
                        goto error;
                    }
C
Chris Lalancette 已提交
333
                    if (virStrncpy(cpy_temp, p, len, sizeof(cpy_temp)) == NULL) {
334 335
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("MAC address %s too long for destination"), p);
C
Chris Lalancette 已提交
336 337
                        goto error;
                    }
338
                    if (virMacAddrParse(cpy_temp, &net->mac) < 0) {
339 340
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong MAC address"));
341 342 343 344 345 346
                        goto error;
                    }
                }
                p = ++next;
            } while (p < token + strlen(token));

347 348 349 350 351
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

352 353 354 355
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

356 357
    VIR_FREE(temp);

358
    return 0;
359
no_memory:
360
    virReportOOMError();
361
error:
362
    VIR_FREE(temp);
363
    virDomainNetDefFree(net);
364
    return -1;
365 366 367
}


368 369 370 371 372 373 374
/* utility function to replace 'from' by 'to' in 'str' */
static char*
openvz_replace(const char* str,
               const char* from,
               const char* to) {
    const char* offset = NULL;
    const char* str_start = str;
375 376
    int to_len;
    int from_len;
377 378
    virBuffer buf = VIR_BUFFER_INITIALIZER;

379
    if ((!from) || (!to))
380
        return NULL;
381 382
    from_len = strlen(from);
    to_len = strlen(to);
383

E
Eric Blake 已提交
384
    while ((offset = strstr(str_start, from)))
385 386 387 388 389 390
    {
        virBufferAdd(&buf, str_start, offset-str_start);
        virBufferAdd(&buf, to, to_len);
        str_start = offset + from_len;
    }

391
    virBufferAdd(&buf, str_start, -1);
392

393 394 395 396
    if (virBufferError(&buf)) {
        virBufferFreeAndReset(&buf);
        return NULL;
    }
397 398 399 400 401

    return virBufferContentAndReset(&buf);
}


402
static int
403
openvzReadFSConf(virDomainDefPtr def,
404 405 406
                 int veid) {
    int ret;
    virDomainFSDefPtr fs = NULL;
407 408
    char *veid_str = NULL;
    char *temp = NULL;
409 410
    const char *param;
    unsigned long long barrier, limit;
411

412
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
413
    if (ret < 0) {
414 415 416
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'OSTEMPLATE' from config for container %d"),
                       veid);
417 418 419 420 421 422 423
        goto error;
    } else if (ret > 0) {
        if (VIR_ALLOC(fs) < 0)
            goto no_memory;

        fs->type = VIR_DOMAIN_FS_TYPE_TEMPLATE;
        fs->src = strdup(temp);
424 425
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
426
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
427
        if (ret <= 0) {
428 429 430
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read 'VE_PRIVATE' from config for container %d"),
                           veid);
431 432
            goto error;
        }
433

434
        if (VIR_ALLOC(fs) < 0)
435 436
            goto no_memory;

437 438
        if (virAsprintf(&veid_str, "%d", veid) < 0)
            goto no_memory;
439 440 441

        fs->type = VIR_DOMAIN_FS_TYPE_MOUNT;
        fs->src = openvz_replace(temp, "$VEID", veid_str);
442

443
        VIR_FREE(veid_str);
444 445
    }

446 447
    fs->dst = strdup("/");

448 449 450 451
    param = "DISKSPACE";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret > 0) {
        if (openvzParseBarrierLimit(temp, &barrier, &limit)) {
452 453 454
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read '%s' from config for container %d"),
                           param, veid);
455 456 457
            goto error;
        } else {
            /* Ensure that we can multiply by 1024 without overflowing. */
458 459
            if (barrier > ULLONG_MAX / 1024 ||
                limit > ULLONG_MAX / 1024 ) {
460 461
                virReportSystemError(VIR_ERR_OVERFLOW, "%s",
                                     _("Unable to parse quota"));
462 463 464 465 466 467 468
                goto error;
            }
            fs->space_soft_limit = barrier * 1024; /* unit is bytes */
            fs->space_hard_limit = limit * 1024;   /* unit is bytes */
        }
    }

469 470 471 472 473 474 475 476
    if (fs->src == NULL || fs->dst == NULL)
        goto no_memory;

    if (VIR_REALLOC_N(def->fss, def->nfss + 1) < 0)
        goto no_memory;
    def->fss[def->nfss++] = fs;
    fs = NULL;

477 478
    VIR_FREE(temp);

479 480
    return 0;
no_memory:
481
    virReportOOMError();
482
error:
483
    VIR_FREE(temp);
484 485 486 487 488
    virDomainFSDefFree(fs);
    return -1;
}


489 490 491 492 493 494 495
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
    int ret;
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
496
    long kb_per_pages;
497

498 499
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
500 501 502 503 504 505
        goto error;

    /* Memory allocation guarantee */
    param = "VMGUARPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
506 507 508
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
509 510 511 512
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
        if (ret < 0) {
513 514 515
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier of '%s' "
                             "from config for container %d"), param, veid);
516 517 518 519 520 521 522 523 524 525 526 527
            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) {
528 529 530
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
531 532 533 534
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
        if (ret < 0) {
535 536 537
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier and limit of '%s' "
                             "from config for container %d"), param, veid);
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
            goto error;
        }
        if (barrier == LONG_MAX)
            def->mem.soft_limit = 0ull;
        else
            def->mem.soft_limit = barrier * kb_per_pages;

        if (limit == LONG_MAX)
            def->mem.hard_limit = 0ull;
        else
            def->mem.hard_limit = limit * kb_per_pages;
    }

    ret = 0;
error:
    VIR_FREE(temp);
    return ret;
}


558 559 560 561 562 563
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
564

565
    virDomainObjListDeinit(&driver->domains);
566
    virCapabilitiesFree(driver->caps);
567
    VIR_FREE(driver);
568
}
D
Daniel Veillard 已提交
569 570 571



572
int openvzLoadDomains(struct openvz_driver *driver) {
573
    int veid, ret;
574
    char *status;
575
    char uuidstr[VIR_UUID_STRING_BUFLEN];
576
    virDomainObjPtr dom = NULL;
577
    char *temp = NULL;
E
Eric Blake 已提交
578 579 580
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
581

582 583
    if (openvzAssignUUIDs() < 0)
        return -1;
584

E
Eric Blake 已提交
585 586 587 588
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
589

590 591 592 593 594
    line = outbuf;
    while (line[0] != '\0') {
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
595 596
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Failed to parse vzlist output"));
597
            goto cleanup;
598
        }
599
        *line++ = '\0';
600

601
        if (VIR_ALLOC(dom) < 0)
602
            goto no_memory;
603

604
        if (virMutexInit(&dom->lock) < 0) {
605 606
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot initialize mutex"));
607 608 609 610
            VIR_FREE(dom);
            goto cleanup;
        }

611 612
        virDomainObjLock(dom);

613 614
        if (VIR_ALLOC(dom->def) < 0)
            goto no_memory;
615

616 617
        dom->def->virtType = VIR_DOMAIN_VIRT_OPENVZ;

J
Jiri Denemark 已提交
618 619 620 621 622 623 624
        if (STREQ(status, "stopped")) {
            virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
                                 VIR_DOMAIN_SHUTOFF_UNKNOWN);
        } else {
            virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
                                 VIR_DOMAIN_RUNNING_UNKNOWN);
        }
625

626
        dom->refs = 1;
627
        dom->pid = veid;
J
Jiri Denemark 已提交
628 629 630 631
        if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_SHUTOFF)
            dom->def->id = -1;
        else
            dom->def->id = veid;
632 633
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
634

635
        if (virAsprintf(&dom->def->name, "%i", veid) < 0)
636
            goto no_memory;
637

638
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
639
        ret = virUUIDParse(uuidstr, dom->def->uuid);
640

641
        if (ret == -1) {
642 643
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
644
            goto cleanup;
645
        }
646

647 648 649 650
        if (!(dom->def->os.type = strdup("exe")))
            goto no_memory;
        if (!(dom->def->os.init = strdup("/sbin/init")))
            goto no_memory;
651

652
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
653
        if (ret < 0) {
654 655 656
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
657
            goto cleanup;
658
        } else if (ret > 0) {
E
Eric Blake 已提交
659
            dom->def->maxvcpus = strtoI(temp);
660 661
        }

E
Eric Blake 已提交
662 663 664
        if (ret == 0 || dom->def->maxvcpus == 0)
            dom->def->maxvcpus = openvzGetNodeCPUs();
        dom->def->vcpus = dom->def->maxvcpus;
665

666
        /* XXX load rest of VM config data .... */
667

668 669
        openvzReadNetworkConf(dom->def, veid);
        openvzReadFSConf(dom->def, veid);
670
        openvzReadMemConf(dom->def, veid);
671

672
        virUUIDFormat(dom->def->uuid, uuidstr);
673
        if (virHashLookup(driver->domains.objs, uuidstr)) {
674 675 676 677
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Duplicate container UUID %s detected for %d"),
                           uuidstr,
                           veid);
678 679 680
            goto cleanup;
        }
        if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0) {
681 682
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not add UUID for container %d"), veid);
683
            goto cleanup;
684
        }
685

686
        virDomainObjUnlock(dom);
687
        dom = NULL;
688
    }
689

E
Eric Blake 已提交
690
    virCommandFree(cmd);
691
    VIR_FREE(temp);
E
Eric Blake 已提交
692
    VIR_FREE(outbuf);
693

694
    return 0;
695

696
 no_memory:
697
    virReportOOMError();
698

699
 cleanup:
E
Eric Blake 已提交
700
    virCommandFree(cmd);
701
    VIR_FREE(temp);
E
Eric Blake 已提交
702
    VIR_FREE(outbuf);
703
    /* dom hasn't been shared yet, so unref should return 0 */
704
    if (dom)
705
        ignore_value(virDomainObjUnref(dom));
706
    return -1;
707 708
}

709 710 711 712 713
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

714
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
715 716 717 718
        return 0;

    return nodeinfo.cpus;
}
719

720 721
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
722
{
723
    char * temp_file = NULL;
724 725 726 727
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
728

729
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
730
        virReportOOMError();
731
        return -1;
732
    }
733

734 735
    fp = fopen(conf_file, "r");
    if (fp == NULL)
736
        goto error;
737 738
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
739
        goto error;
740 741
    }

E
Eric Blake 已提交
742
    while (1) {
743
        if (getline(&line, &line_size, fp) <= 0)
744 745
            break;

746
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
747 748 749 750 751 752 753 754 755 756 757 758
            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;

759
    if (VIR_FCLOSE(fp) < 0)
760
        goto error;
761
    if (VIR_CLOSE(temp_fd) < 0)
762 763 764 765 766
        goto error;

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

767 768
    VIR_FREE(line);

769 770 771
    return 0;

error:
772 773
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
774
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
775
    if (temp_file)
776 777
        unlink(temp_file);
    VIR_FREE(temp_file);
778 779 780
    return -1;
}

781
int
782 783
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
784 785
    char *conf_file;
    int ret;
786

787
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
788 789
        return -1;

790 791 792
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
793 794
}

795 796 797
/*
 * value will be freed before a new value is assigned to it, the caller is
 * responsible for freeing it afterwards.
798 799
 *
 * Returns <0 on error, 0 if not found, 1 if found.
800
 */
801
int
802
openvzReadConfigParam(const char *conf_file, const char *param, char **value)
803
{
804 805 806
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
807 808
    int err = 0;
    char *sf, *token, *saveptr = NULL;
809

810 811
    fp = fopen(conf_file, "r");
    if (fp == NULL)
812 813
        return -1;

814
    VIR_FREE(*value);
815 816 817 818 819 820
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

821 822 823 824 825 826
        if (! STREQLEN(line, param, strlen(param)))
            continue;

        sf = line + strlen(param);
        if (*sf++ != '=') continue;

827
        saveptr = NULL;
828 829 830 831 832 833
        if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
            VIR_FREE(*value);
            *value = strdup(token);
            if (*value == NULL) {
                err = 1;
                break;
834
            }
835 836
            /* keep going - last entry wins */
        }
837
    }
838 839
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
840

841
    return err ? -1 : *value ? 1 : 0;
842 843
}

844
/*
845 846 847 848 849 850 851 852 853 854
 * Read parameter from container config
 *
 * value will be freed before a new value is assined to it, the caller is
 * responsible for freeing it afterwards.
 *
 * sample: 133, "OSTEMPLATE", &value
 * return: -1 - error
 *          0 - don't found
 *          1 - OK
 */
855
int
856
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
857
{
858 859
    char *conf_file;
    int ret;
860

861
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
862 863
        return -1;

864
    ret = openvzReadConfigParam(conf_file, param, value);
865 866
    VIR_FREE(conf_file);
    return ret;
867 868 869 870 871
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
872 873 874 875
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
876 877
    int bytes_read;

878 879
    fp = fopen(from_path, "r");
    if (fp == NULL)
880 881 882
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
883
        VIR_FORCE_FCLOSE(fp);
884 885 886
        return -1;
    }

E
Eric Blake 已提交
887
    while (1) {
888
        if (getline(&line, &line_size, fp) <= 0)
889 890 891 892 893 894 895
            break;

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

896
    if (VIR_FCLOSE(fp) < 0)
897
        goto error;
898
    if (VIR_CLOSE(copy_fd) < 0)
899 900
        goto error;

901 902
    VIR_FREE(line);

903 904 905
    return 0;

error:
906 907
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
908
    VIR_FORCE_CLOSE(copy_fd);
909 910 911 912 913 914 915 916 917 918 919
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
920 921 922
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
923
    char *conf_file = NULL;
924 925
    int ret = -1;

926
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
927 928 929 930 931 932
        goto cleanup;

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

933 934
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
935
        virReportOOMError();
936
        goto cleanup;
937
    }
938

939
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
940 941 942 943 944 945 946 947 948
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
949
    VIR_FREE(configfile_value);
950
    VIR_FREE(conf_file);
951 952 953
    return ret;
}

954
/* Locate config file of container
955 956
 * return -1 - error
 *         0 - OK */
957
static int
958
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
959
{
960
    char *confdir;
961 962 963 964 965 966
    int ret = 0;

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

967 968 969
    if (virAsprintf(conffile, "%s/%d.%s", confdir, vpsid,
                    ext ? ext : "conf") < 0) {
        virReportOOMError();
970
        ret = -1;
971
    }
972 973 974 975 976

    VIR_FREE(confdir);
    return ret;
}

977 978
static char *
openvzLocateConfDir(void)
979 980 981 982
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
983 984
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
985
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
986
        i++;
987 988 989 990 991 992
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
993
int
994
openvz_readline(int fd, char *ptr, int maxlen)
995 996 997 998
{
    int n, rc;
    char c;

E
Eric Blake 已提交
999 1000
    for (n = 1; n < maxlen; n++) {
        if ( (rc = read(fd, &c, 1)) == 1) {
1001
            *ptr++ = c;
E
Eric Blake 已提交
1002
            if (c == '\n')
1003
                break;
E
Eric Blake 已提交
1004 1005
        } else if (rc == 0) {
            if (n == 1)
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

1017
static int
1018
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
1019
{
1020
    char *conf_file;
1021 1022
    char *line = NULL;
    size_t line_size = 0;
1023
    char *saveptr = NULL;
1024 1025
    char *uuidbuf;
    char *iden;
1026
    FILE *fp;
1027
    int retval = -1;
1028

1029
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1030
        return -1;
1031

1032 1033
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1034
        goto cleanup;
1035

E
Eric Blake 已提交
1036
    while (1) {
1037 1038 1039 1040 1041 1042 1043
        if (getline(&line, &line_size, fp) < 0) {
            if (feof(fp)) { /* EOF, UUID was not found */
                uuidstr[0] = 0;
                break;
            } else {
                goto cleanup;
            }
1044 1045
        }

1046 1047 1048 1049
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
1050
            if (virStrcpy(uuidstr, uuidbuf, len) == NULL) {
1051 1052
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("invalid uuid %s"), uuidbuf);
1053 1054
                goto cleanup;
            }
1055 1056 1057
            break;
        }
    }
1058 1059
    retval = 0;
cleanup:
1060 1061
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
1062
    VIR_FREE(conf_file);
1063

C
Chris Lalancette 已提交
1064
    return retval;
1065 1066 1067 1068 1069
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
1070 1071
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
1072
{
1073
    char *conf_file;
1074
    char uuidstr[VIR_UUID_STRING_BUFLEN];
1075
    FILE *fp = NULL;
1076
    int ret = -1;
1077 1078 1079

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

1081
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1082
        return -1;
1083

1084
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1085
        goto cleanup;
1086

J
Jim Meyering 已提交
1087
    if (uuidstr[0] == 0) {
1088
        fp = fopen(conf_file, "a"); /* append */
1089
        if (fp == NULL)
1090
            goto cleanup;
1091

1092 1093
        virUUIDFormat(uuid, uuidstr);

1094
        /* Record failure if fprintf or VIR_FCLOSE fails,
1095
           and be careful always to close the stream.  */
1096 1097
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
            (VIR_FCLOSE(fp) == EOF))
1098
            goto cleanup;
1099 1100
    }

1101 1102
    ret = 0;
cleanup:
1103
    VIR_FORCE_FCLOSE(fp);
1104 1105
    VIR_FREE(conf_file);
    return ret;
1106 1107
}

1108 1109 1110 1111
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1112
    if (virUUIDGenerate(uuid)) {
1113 1114
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
1115 1116
        return -1;
    }
1117 1118 1119 1120

    return openvzSetDefinedUUID(vpsid, uuid);
}

1121 1122 1123 1124 1125 1126 1127
/*
 * 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.
 *
 */

1128
static int openvzAssignUUIDs(void)
1129 1130 1131 1132
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1133 1134 1135
    int vpsid;
    char *ext;
    int ret = 0;
1136 1137

    conf_dir = openvzLocateConfDir();
1138 1139
    if (conf_dir == NULL)
        return -1;
1140 1141

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1142
    if (dp == NULL) {
1143
        VIR_FREE(conf_dir);
1144 1145 1146
        return 0;
    }

1147
    errno = 0;
E
Eric Blake 已提交
1148
    while ((dent = readdir(dp))) {
1149 1150 1151
        if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
            *ext++ != '.' ||
            STRNEQ(ext, "conf"))
1152
            continue;
E
Eric Blake 已提交
1153
        if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
1154
            openvzSetUUID(vpsid);
1155 1156 1157
        errno = 0;
    }
    if (errno) {
1158 1159
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to scan configuration directory"));
1160
        ret = -1;
1161
    }
1162

1163
    closedir(dp);
1164
    VIR_FREE(conf_dir);
1165
    return ret;
1166
}
1167 1168 1169 1170 1171 1172 1173 1174


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1175 1176
    virCommandPtr cmd;
    char *outbuf;
1177
    char *temp;
1178
    int veid;
1179
    bool ok;
1180

E
Eric Blake 已提交
1181 1182 1183 1184 1185
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1186 1187 1188
        return -1;
    }

E
Eric Blake 已提交
1189
    virCommandFree(cmd);
1190
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1191
    VIR_FREE(outbuf);
1192

1193 1194 1195
    if (ok && veid >= 0)
        return veid;

1196 1197
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1198 1199
    return -1;
}