openvz_conf.c 30.1 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 20 21 22
 *
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
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
#include "ignore-value.h"
57

58 59
#define VIR_FROM_THIS VIR_FROM_OPENVZ

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

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
66

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

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

75 76 77
    return val;
}

78 79

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

    if (retversion)
        *retversion = 0;

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

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

97 98 99 100
    tmp = help;

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

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

    if (retversion)
        *retversion = version;

    ret = 0;

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

    return ret;
}

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

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

    return 0;
}


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 172
/* 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;
}


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

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

    uname(&utsname);

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

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

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

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

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

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

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


223
int
224
openvzReadNetworkConf(virDomainDefPtr def,
225
                      int veid) {
226
    int ret;
227
    virDomainNetDefPtr net = NULL;
228
    char *temp = NULL;
229 230 231 232 233 234 235
    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
     */
236
    ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
237
    if (ret < 0) {
238 239 240
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read 'IP_ADDRESS' from config for container %d"),
                    veid);
241 242 243 244
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, " ", &saveptr);
        while (token != NULL) {
245
            if (VIR_ALLOC(net) < 0)
246 247 248 249 250 251 252 253
                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;

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

259 260 261 262 263 264 265 266 267
            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 ';'
     */
268
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
269
    if (ret < 0) {
270 271 272
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read 'NETIF' from config for container %d"),
                    veid);
273 274 275 276 277
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
            /*add new device to list*/
278
            if (VIR_ALLOC(net) < 0)
279 280 281 282
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

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

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

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

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

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

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

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

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

357 358
    VIR_FREE(temp);

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


369 370 371 372 373 374 375
/* 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;
376 377
    int to_len;
    int from_len;
378 379
    virBuffer buf = VIR_BUFFER_INITIALIZER;

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

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

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

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

    return virBufferContentAndReset(&buf);
}


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

413
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
414
    if (ret < 0) {
415
        openvzError(VIR_ERR_INTERNAL_ERROR,
416
                    _("Could not read 'OSTEMPLATE' from config for container %d"),
417 418 419 420 421 422 423 424
                    veid);
        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);
425 426
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
427
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
428
        if (ret <= 0) {
429
            openvzError(VIR_ERR_INTERNAL_ERROR,
430
                        _("Could not read 'VE_PRIVATE' from config for container %d"),
431 432 433
                        veid);
            goto error;
        }
434

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

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

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

444
        VIR_FREE(veid_str);
445 446
    }

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

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

471 472 473 474 475 476 477 478
    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;

479 480
    VIR_FREE(temp);

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


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

500 501
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
        goto error;

    /* Memory allocation guarantee */
    param = "VMGUARPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read '%s' from config for container %d"),
                    param, veid);
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
        if (ret < 0) {
            openvzError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse  barrier of '%s' "
                          "from config for container %d"), param, veid);
            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) {
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read '%s' from config for container %d"),
                    param, veid);
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
        if (ret < 0) {
            openvzError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse barrier and limit of '%s' "
                          "from config for container %d"), param, veid);
            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;
}


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

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



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

584 585
    if (openvzAssignUUIDs() < 0)
        return -1;
586

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

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

603
        if (VIR_ALLOC(dom) < 0)
604
            goto no_memory;
605

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

613 614
        virDomainObjLock(dom);

615 616
        if (VIR_ALLOC(dom->def) < 0)
            goto no_memory;
617

618 619
        dom->def->virtType = VIR_DOMAIN_VIRT_OPENVZ;

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

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

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

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

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

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

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

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

668
        /* XXX load rest of VM config data .... */
669

670 671
        openvzReadNetworkConf(dom->def, veid);
        openvzReadFSConf(dom->def, veid);
672
        openvzReadMemConf(dom->def, veid);
673

674 675
        virUUIDFormat(dom->def->uuid, uuidstr);
        if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0)
676
            goto cleanup;
677

678
        virDomainObjUnlock(dom);
679
        dom = NULL;
680
    }
681

E
Eric Blake 已提交
682
    virCommandFree(cmd);
683
    VIR_FREE(temp);
E
Eric Blake 已提交
684
    VIR_FREE(outbuf);
685

686
    return 0;
687

688
 no_memory:
689
    virReportOOMError();
690

691
 cleanup:
E
Eric Blake 已提交
692
    virCommandFree(cmd);
693
    VIR_FREE(temp);
E
Eric Blake 已提交
694
    VIR_FREE(outbuf);
695
    /* dom hasn't been shared yet, so unref should return 0 */
696
    if (dom)
697
        ignore_value(virDomainObjUnref(dom));
698
    return -1;
699 700
}

701 702 703 704 705
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

706
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
707 708 709 710
        return 0;

    return nodeinfo.cpus;
}
711

712 713
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
714
{
715
    char * temp_file = NULL;
716 717 718 719
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
720

721
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
722
        virReportOOMError();
723
        return -1;
724
    }
725

726 727
    fp = fopen(conf_file, "r");
    if (fp == NULL)
728
        goto error;
729 730
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
731
        goto error;
732 733
    }

E
Eric Blake 已提交
734
    while (1) {
735
        if (getline(&line, &line_size, fp) <= 0)
736 737
            break;

738
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
739 740 741 742 743 744 745 746 747 748 749 750
            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;

751
    if (VIR_FCLOSE(fp) < 0)
752
        goto error;
753
    if (VIR_CLOSE(temp_fd) < 0)
754 755 756 757 758
        goto error;

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

759 760
    VIR_FREE(line);

761 762 763
    return 0;

error:
764 765
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
766
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
767
    if (temp_file)
768 769
        unlink(temp_file);
    VIR_FREE(temp_file);
770 771 772
    return -1;
}

773
int
774 775
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
776 777
    char *conf_file;
    int ret;
778

779
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
780 781
        return -1;

782 783 784
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
785 786
}

787 788 789
/*
 * value will be freed before a new value is assigned to it, the caller is
 * responsible for freeing it afterwards.
790 791
 *
 * Returns <0 on error, 0 if not found, 1 if found.
792
 */
793
int
794
openvzReadConfigParam(const char *conf_file, const char *param, char **value)
795
{
796 797 798
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
799 800
    int err = 0;
    char *sf, *token, *saveptr = NULL;
801

802 803
    fp = fopen(conf_file, "r");
    if (fp == NULL)
804 805
        return -1;

806
    VIR_FREE(*value);
807 808 809 810 811 812
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

813 814 815 816 817 818
        if (! STREQLEN(line, param, strlen(param)))
            continue;

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

819
        saveptr = NULL;
820 821 822 823 824 825
        if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
            VIR_FREE(*value);
            *value = strdup(token);
            if (*value == NULL) {
                err = 1;
                break;
826
            }
827 828
            /* keep going - last entry wins */
        }
829
    }
830 831
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
832

833
    return err ? -1 : *value ? 1 : 0;
834 835
}

836
/*
837 838 839 840 841 842 843 844 845 846
 * 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
 */
847
int
848
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
849
{
850 851
    char *conf_file;
    int ret;
852

853
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
854 855
        return -1;

856
    ret = openvzReadConfigParam(conf_file, param, value);
857 858
    VIR_FREE(conf_file);
    return ret;
859 860 861 862 863
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
864 865 866 867
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
868 869
    int bytes_read;

870 871
    fp = fopen(from_path, "r");
    if (fp == NULL)
872 873 874
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
875
        VIR_FORCE_FCLOSE(fp);
876 877 878
        return -1;
    }

E
Eric Blake 已提交
879
    while (1) {
880
        if (getline(&line, &line_size, fp) <= 0)
881 882 883 884 885 886 887
            break;

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

888
    if (VIR_FCLOSE(fp) < 0)
889
        goto error;
890
    if (VIR_CLOSE(copy_fd) < 0)
891 892
        goto error;

893 894
    VIR_FREE(line);

895 896 897
    return 0;

error:
898 899
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
900
    VIR_FORCE_CLOSE(copy_fd);
901 902 903 904 905 906 907 908 909 910 911
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
912 913 914
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
915
    char *conf_file = NULL;
916 917
    int ret = -1;

918
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
919 920 921 922 923 924
        goto cleanup;

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

925 926
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
927
        virReportOOMError();
928
        goto cleanup;
929
    }
930

931
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
932 933 934 935 936 937 938 939 940
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
941
    VIR_FREE(configfile_value);
942
    VIR_FREE(conf_file);
943 944 945
    return ret;
}

946
/* Locate config file of container
947 948
 * return -1 - error
 *         0 - OK */
949
static int
950
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
951
{
952
    char *confdir;
953 954 955 956 957 958
    int ret = 0;

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

959 960 961
    if (virAsprintf(conffile, "%s/%d.%s", confdir, vpsid,
                    ext ? ext : "conf") < 0) {
        virReportOOMError();
962
        ret = -1;
963
    }
964 965 966 967 968

    VIR_FREE(confdir);
    return ret;
}

969 970
static char *
openvzLocateConfDir(void)
971 972 973 974
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
975 976
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
977
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
978
        i++;
979 980 981 982 983 984
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
985
int
986
openvz_readline(int fd, char *ptr, int maxlen)
987 988 989 990
{
    int n, rc;
    char c;

E
Eric Blake 已提交
991 992
    for (n = 1; n < maxlen; n++) {
        if ( (rc = read(fd, &c, 1)) == 1) {
993
            *ptr++ = c;
E
Eric Blake 已提交
994
            if (c == '\n')
995
                break;
E
Eric Blake 已提交
996 997
        } else if (rc == 0) {
            if (n == 1)
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

1009
static int
1010
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
1011
{
1012
    char *conf_file;
1013 1014
    char *line = NULL;
    size_t line_size = 0;
1015
    char *saveptr = NULL;
1016 1017
    char *uuidbuf;
    char *iden;
1018
    FILE *fp;
1019
    int retval = -1;
1020

1021
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1022
        return -1;
1023

1024 1025
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1026
        goto cleanup;
1027

E
Eric Blake 已提交
1028
    while (1) {
1029 1030 1031 1032 1033 1034 1035
        if (getline(&line, &line_size, fp) < 0) {
            if (feof(fp)) { /* EOF, UUID was not found */
                uuidstr[0] = 0;
                break;
            } else {
                goto cleanup;
            }
1036 1037
        }

1038 1039 1040 1041
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
1042 1043 1044 1045 1046
            if (virStrcpy(uuidstr, uuidbuf, len) == NULL) {
                openvzError(VIR_ERR_INTERNAL_ERROR,
                            _("invalid uuid %s"), uuidbuf);
                goto cleanup;
            }
1047 1048 1049
            break;
        }
    }
1050 1051
    retval = 0;
cleanup:
1052 1053
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
1054
    VIR_FREE(conf_file);
1055

C
Chris Lalancette 已提交
1056
    return retval;
1057 1058 1059 1060 1061
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
1062 1063
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
1064
{
1065
    char *conf_file;
1066
    char uuidstr[VIR_UUID_STRING_BUFLEN];
1067
    FILE *fp = NULL;
1068
    int ret = -1;
1069 1070 1071

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

1073
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1074
        return -1;
1075

1076
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1077
        goto cleanup;
1078

J
Jim Meyering 已提交
1079
    if (uuidstr[0] == 0) {
1080
        fp = fopen(conf_file, "a"); /* append */
1081
        if (fp == NULL)
1082
            goto cleanup;
1083

1084 1085
        virUUIDFormat(uuid, uuidstr);

1086
        /* Record failure if fprintf or VIR_FCLOSE fails,
1087
           and be careful always to close the stream.  */
1088 1089
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
            (VIR_FCLOSE(fp) == EOF))
1090
            goto cleanup;
1091 1092
    }

1093 1094
    ret = 0;
cleanup:
1095
    VIR_FORCE_FCLOSE(fp);
1096 1097
    VIR_FREE(conf_file);
    return ret;
1098 1099
}

1100 1101 1102 1103
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1104
    if (virUUIDGenerate(uuid)) {
1105 1106
        openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("Failed to generate UUID"));
1107 1108
        return -1;
    }
1109 1110 1111 1112

    return openvzSetDefinedUUID(vpsid, uuid);
}

1113 1114 1115 1116 1117 1118 1119
/*
 * 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.
 *
 */

1120
static int openvzAssignUUIDs(void)
1121 1122 1123 1124
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1125 1126 1127
    int vpsid;
    char *ext;
    int ret = 0;
1128 1129

    conf_dir = openvzLocateConfDir();
1130 1131
    if (conf_dir == NULL)
        return -1;
1132 1133

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1134
    if (dp == NULL) {
1135
        VIR_FREE(conf_dir);
1136 1137 1138
        return 0;
    }

1139
    errno = 0;
E
Eric Blake 已提交
1140
    while ((dent = readdir(dp))) {
1141 1142 1143
        if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
            *ext++ != '.' ||
            STRNEQ(ext, "conf"))
1144
            continue;
E
Eric Blake 已提交
1145
        if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
1146
            openvzSetUUID(vpsid);
1147 1148 1149 1150 1151 1152
        errno = 0;
    }
    if (errno) {
        openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("Failed to scan configuration directory"));
        ret = -1;
1153
    }
1154

1155
    closedir(dp);
1156
    VIR_FREE(conf_dir);
1157
    return ret;
1158
}
1159 1160 1161 1162 1163 1164 1165 1166


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1167 1168
    virCommandPtr cmd;
    char *outbuf;
1169
    char *temp;
1170
    int veid;
1171
    bool ok;
1172

E
Eric Blake 已提交
1173 1174 1175 1176 1177
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1178 1179 1180
        return -1;
    }

E
Eric Blake 已提交
1181
    virCommandFree(cmd);
1182
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1183
    VIR_FREE(outbuf);
1184

1185 1186 1187
    if (ok && veid >= 0)
        return veid;

1188 1189
    openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                _("Failed to parse vzlist output"));
1190 1191
    return -1;
}