openvz_conf.c 30.2 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
20
 * License along with this library.  If not, see
O
Osier Yang 已提交
21
 * <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
            if (barrier > ULLONG_MAX / 1024 ||
459
                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 602
        if (!(dom = virDomainObjNew(driver->caps)))
             goto cleanup;
603

604 605
        if (VIR_ALLOC(dom->def) < 0)
            goto no_memory;
606

607 608
        dom->def->virtType = VIR_DOMAIN_VIRT_OPENVZ;

J
Jiri Denemark 已提交
609 610 611 612 613 614 615
        if (STREQ(status, "stopped")) {
            virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
                                 VIR_DOMAIN_SHUTOFF_UNKNOWN);
        } else {
            virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
                                 VIR_DOMAIN_RUNNING_UNKNOWN);
        }
616

617
        dom->pid = veid;
J
Jiri Denemark 已提交
618 619 620 621
        if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_SHUTOFF)
            dom->def->id = -1;
        else
            dom->def->id = veid;
622 623
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
624

625
        if (virAsprintf(&dom->def->name, "%i", veid) < 0)
626
            goto no_memory;
627

628
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
629
        ret = virUUIDParse(uuidstr, dom->def->uuid);
630

631
        if (ret == -1) {
632 633
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
634
            goto cleanup;
635
        }
636

637 638 639 640
        if (!(dom->def->os.type = strdup("exe")))
            goto no_memory;
        if (!(dom->def->os.init = strdup("/sbin/init")))
            goto no_memory;
641

642
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
643
        if (ret < 0) {
644 645 646
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
647
            goto cleanup;
648
        } else if (ret > 0) {
E
Eric Blake 已提交
649
            dom->def->maxvcpus = strtoI(temp);
650 651
        }

E
Eric Blake 已提交
652 653 654
        if (ret == 0 || dom->def->maxvcpus == 0)
            dom->def->maxvcpus = openvzGetNodeCPUs();
        dom->def->vcpus = dom->def->maxvcpus;
655

656
        /* XXX load rest of VM config data .... */
657

658 659
        openvzReadNetworkConf(dom->def, veid);
        openvzReadFSConf(dom->def, veid);
660
        openvzReadMemConf(dom->def, veid);
661

662
        virUUIDFormat(dom->def->uuid, uuidstr);
663
        if (virHashLookup(driver->domains.objs, uuidstr)) {
664 665 666 667
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Duplicate container UUID %s detected for %d"),
                           uuidstr,
                           veid);
668 669 670
            goto cleanup;
        }
        if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0) {
671 672
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not add UUID for container %d"), veid);
673
            goto cleanup;
674
        }
675

676
        virDomainObjUnlock(dom);
677
        dom = NULL;
678
    }
679

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

684
    return 0;
685

686
 no_memory:
687
    virReportOOMError();
688

689
 cleanup:
E
Eric Blake 已提交
690
    virCommandFree(cmd);
691
    VIR_FREE(temp);
E
Eric Blake 已提交
692
    VIR_FREE(outbuf);
693
    virObjectUnref(dom);
694
    return -1;
695 696
}

697 698 699 700 701
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

702
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
703 704 705 706
        return 0;

    return nodeinfo.cpus;
}
707

708 709
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
710
{
711
    char * temp_file = NULL;
712 713 714 715
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
716

717
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
718
        virReportOOMError();
719
        return -1;
720
    }
721

722 723
    fp = fopen(conf_file, "r");
    if (fp == NULL)
724
        goto error;
725 726
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
727
        goto error;
728 729
    }

E
Eric Blake 已提交
730
    while (1) {
731
        if (getline(&line, &line_size, fp) <= 0)
732 733
            break;

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

747
    if (VIR_FCLOSE(fp) < 0)
748
        goto error;
749
    if (VIR_CLOSE(temp_fd) < 0)
750 751 752 753 754
        goto error;

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

755 756
    VIR_FREE(line);

757 758 759
    return 0;

error:
760 761
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
762
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
763
    if (temp_file)
764 765
        unlink(temp_file);
    VIR_FREE(temp_file);
766 767 768
    return -1;
}

769
int
770 771
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
772 773
    char *conf_file;
    int ret;
774

775
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
776 777
        return -1;

778 779 780
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
781 782
}

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

798 799
    fp = fopen(conf_file, "r");
    if (fp == NULL)
800 801
        return -1;

802
    VIR_FREE(*value);
803 804 805 806 807 808
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

809 810 811 812 813 814
        if (! STREQLEN(line, param, strlen(param)))
            continue;

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

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

829
    return err ? -1 : *value ? 1 : 0;
830 831
}

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

849
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
850 851
        return -1;

852
    ret = openvzReadConfigParam(conf_file, param, value);
853 854
    VIR_FREE(conf_file);
    return ret;
855 856 857 858 859
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
860 861 862 863
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
864 865
    int bytes_read;

866 867
    fp = fopen(from_path, "r");
    if (fp == NULL)
868 869 870
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
871
        VIR_FORCE_FCLOSE(fp);
872 873 874
        return -1;
    }

E
Eric Blake 已提交
875
    while (1) {
876
        if (getline(&line, &line_size, fp) <= 0)
877 878 879 880 881 882 883
            break;

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

884
    if (VIR_FCLOSE(fp) < 0)
885
        goto error;
886
    if (VIR_CLOSE(copy_fd) < 0)
887 888
        goto error;

889 890
    VIR_FREE(line);

891 892 893
    return 0;

error:
894 895
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
896
    VIR_FORCE_CLOSE(copy_fd);
897 898 899 900 901 902 903 904 905 906 907
    return -1;
}

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

914
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
915 916 917 918 919 920
        goto cleanup;

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

921 922
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
923
        virReportOOMError();
924
        goto cleanup;
925
    }
926

927
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
928 929 930 931 932 933 934 935 936
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
937
    VIR_FREE(configfile_value);
938
    VIR_FREE(conf_file);
939 940 941
    return ret;
}

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

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

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

    VIR_FREE(confdir);
    return ret;
}

965 966
static char *
openvzLocateConfDir(void)
967 968 969 970
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
971 972
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
973
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
974
        i++;
975 976 977 978 979 980
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
981
int
982
openvz_readline(int fd, char *ptr, int maxlen)
983 984 985 986
{
    int n, rc;
    char c;

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

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

1017
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1018
        return -1;
1019

1020 1021
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1022
        goto cleanup;
1023

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

1034 1035 1036 1037
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

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

C
Chris Lalancette 已提交
1052
    return retval;
1053 1054 1055 1056 1057
}

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

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

1069
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1070
        return -1;
1071

1072
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1073
        goto cleanup;
1074

J
Jim Meyering 已提交
1075
    if (uuidstr[0] == 0) {
1076
        fp = fopen(conf_file, "a"); /* append */
1077
        if (fp == NULL)
1078
            goto cleanup;
1079

1080 1081
        virUUIDFormat(uuid, uuidstr);

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

1089 1090
    ret = 0;
cleanup:
1091
    VIR_FORCE_FCLOSE(fp);
1092 1093
    VIR_FREE(conf_file);
    return ret;
1094 1095
}

1096 1097 1098 1099
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1100
    if (virUUIDGenerate(uuid)) {
1101 1102
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
1103 1104
        return -1;
    }
1105 1106 1107 1108

    return openvzSetDefinedUUID(vpsid, uuid);
}

1109 1110 1111 1112 1113 1114 1115
/*
 * 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.
 *
 */

1116
static int openvzAssignUUIDs(void)
1117 1118 1119 1120
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1121 1122 1123
    int vpsid;
    char *ext;
    int ret = 0;
1124 1125

    conf_dir = openvzLocateConfDir();
1126 1127
    if (conf_dir == NULL)
        return -1;
1128 1129

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1130
    if (dp == NULL) {
1131
        VIR_FREE(conf_dir);
1132 1133 1134
        return 0;
    }

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

1151
    closedir(dp);
1152
    VIR_FREE(conf_dir);
1153
    return ret;
1154
}
1155 1156 1157 1158 1159 1160 1161 1162


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1163 1164
    virCommandPtr cmd;
    char *outbuf;
1165
    char *temp;
1166
    int veid;
1167
    bool ok;
1168

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

E
Eric Blake 已提交
1177
    virCommandFree(cmd);
1178
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1179
    VIR_FREE(outbuf);
1180

1181 1182 1183
    if (ok && veid >= 0)
        return veid;

1184 1185
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1186 1187
    return -1;
}