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/wait.h>
44

45
#include "virerror.h"
46
#include "openvz_conf.h"
47
#include "openvz_util.h"
48
#include "viruuid.h"
49
#include "virbuffer.h"
50
#include "viralloc.h"
51
#include "virutil.h"
52
#include "nodeinfo.h"
E
Eric Blake 已提交
53
#include "virfile.h"
54
#include "vircommand.h"
55

56 57
#define VIR_FROM_THIS VIR_FROM_OPENVZ

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

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
64

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

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

73 74 75
    return val;
}

76 77

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

    if (retversion)
        *retversion = 0;

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

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

95 96 97 98
    tmp = help;

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

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

    if (retversion)
        *retversion = version;

    ret = 0;

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

    return ret;
}

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

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

    return 0;
}


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


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

177 178 179 180 181
virCapsPtr openvzCapsInit(void)
{
    virCapsPtr caps;
    virCapsGuestPtr guest;

182
    if ((caps = virCapabilitiesNew(virArchFromHost(),
183 184 185
                                   0, 0)) == NULL)
        goto no_memory;

186
    if (nodeCapsInitNUMA(caps) < 0)
187 188
        goto no_memory;

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

191 192
    if ((guest = virCapabilitiesAddGuest(caps,
                                         "exe",
193
                                         caps->host.arch,
194 195 196 197 198 199 200 201 202 203 204 205 206 207
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
        goto no_memory;

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

208
    caps->defaultInitPath = "/sbin/init";
209
    caps->defaultConsoleTargetType = openvzDefaultConsoleType;
210 211

    return caps;
212 213 214 215 216 217
no_memory:
    virCapabilitiesFree(caps);
    return NULL;
}


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

249 250 251 252 253
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

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

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

278
            char *p = token;
279 280 281 282 283
            char cpy_temp[32];
            int len;

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

296 297 298
                    if (VIR_ALLOC_N(net->ifname, len+1) < 0)
                        goto no_memory;

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

313 314 315
                    if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
                        goto no_memory;

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

343 344 345 346 347
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

348 349 350 351
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

352 353
    VIR_FREE(temp);

354
    return 0;
355
no_memory:
356
    virReportOOMError();
357
error:
358
    VIR_FREE(temp);
359
    virDomainNetDefFree(net);
360
    return -1;
361 362 363
}


364 365 366 367 368 369 370
/* 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;
371 372
    int to_len;
    int from_len;
373 374
    virBuffer buf = VIR_BUFFER_INITIALIZER;

375
    if ((!from) || (!to))
376
        return NULL;
377 378
    from_len = strlen(from);
    to_len = strlen(to);
379

E
Eric Blake 已提交
380
    while ((offset = strstr(str_start, from)))
381 382 383 384 385 386
    {
        virBufferAdd(&buf, str_start, offset-str_start);
        virBufferAdd(&buf, to, to_len);
        str_start = offset + from_len;
    }

387
    virBufferAdd(&buf, str_start, -1);
388

389 390 391 392
    if (virBufferError(&buf)) {
        virBufferFreeAndReset(&buf);
        return NULL;
    }
393 394 395 396 397

    return virBufferContentAndReset(&buf);
}


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

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

430
        if (VIR_ALLOC(fs) < 0)
431 432
            goto no_memory;

433 434
        if (virAsprintf(&veid_str, "%d", veid) < 0)
            goto no_memory;
435 436 437

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

439
        VIR_FREE(veid_str);
440 441
    }

442 443
    fs->dst = strdup("/");

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

465 466 467 468 469 470 471 472
    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;

473 474
    VIR_FREE(temp);

475 476
    return 0;
no_memory:
477
    virReportOOMError();
478
error:
479
    VIR_FREE(temp);
480 481 482 483 484
    virDomainFSDefFree(fs);
    return -1;
}


485 486 487
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
488
    int ret = -1;
489 490 491
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
492
    long kb_per_pages;
493

494 495
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
496 497 498 499 500 501
        goto error;

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


554 555 556 557 558 559
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
560

561
    virDomainObjListDeinit(&driver->domains);
562
    virCapabilitiesFree(driver->caps);
563
    VIR_FREE(driver);
564
}
D
Daniel Veillard 已提交
565 566 567



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

578 579
    if (openvzAssignUUIDs() < 0)
        return -1;
580

E
Eric Blake 已提交
581 582 583 584
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
585

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

597 598
        if (!(dom = virDomainObjNew(driver->caps)))
             goto cleanup;
599

600 601
        if (VIR_ALLOC(dom->def) < 0)
            goto no_memory;
602

603 604
        dom->def->virtType = VIR_DOMAIN_VIRT_OPENVZ;

J
Jiri Denemark 已提交
605 606 607 608 609 610 611
        if (STREQ(status, "stopped")) {
            virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
                                 VIR_DOMAIN_SHUTOFF_UNKNOWN);
        } else {
            virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
                                 VIR_DOMAIN_RUNNING_UNKNOWN);
        }
612

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

621
        if (virAsprintf(&dom->def->name, "%i", veid) < 0)
622
            goto no_memory;
623

624
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
625
        ret = virUUIDParse(uuidstr, dom->def->uuid);
626

627
        if (ret == -1) {
628 629
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
630
            goto cleanup;
631
        }
632

633 634 635 636
        if (!(dom->def->os.type = strdup("exe")))
            goto no_memory;
        if (!(dom->def->os.init = strdup("/sbin/init")))
            goto no_memory;
637

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

E
Eric Blake 已提交
648 649 650
        if (ret == 0 || dom->def->maxvcpus == 0)
            dom->def->maxvcpus = openvzGetNodeCPUs();
        dom->def->vcpus = dom->def->maxvcpus;
651

652
        /* XXX load rest of VM config data .... */
653

654 655
        openvzReadNetworkConf(dom->def, veid);
        openvzReadFSConf(dom->def, veid);
656
        openvzReadMemConf(dom->def, veid);
657

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

672
        virDomainObjUnlock(dom);
673
        dom = NULL;
674
    }
675

E
Eric Blake 已提交
676
    virCommandFree(cmd);
677
    VIR_FREE(temp);
E
Eric Blake 已提交
678
    VIR_FREE(outbuf);
679

680
    return 0;
681

682
 no_memory:
683
    virReportOOMError();
684

685
 cleanup:
E
Eric Blake 已提交
686
    virCommandFree(cmd);
687
    VIR_FREE(temp);
E
Eric Blake 已提交
688
    VIR_FREE(outbuf);
689
    virObjectUnref(dom);
690
    return -1;
691 692
}

693 694 695 696 697
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

698
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
699 700 701 702
        return 0;

    return nodeinfo.cpus;
}
703

704 705
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
706
{
707
    char * temp_file = NULL;
708 709 710 711
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
712

713
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
714
        virReportOOMError();
715
        return -1;
716
    }
717

718 719
    fp = fopen(conf_file, "r");
    if (fp == NULL)
720
        goto error;
721 722
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
723
        goto error;
724 725
    }

E
Eric Blake 已提交
726
    while (1) {
727
        if (getline(&line, &line_size, fp) <= 0)
728 729
            break;

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

743
    if (VIR_FCLOSE(fp) < 0)
744
        goto error;
745
    if (VIR_CLOSE(temp_fd) < 0)
746 747 748 749 750
        goto error;

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

751 752
    VIR_FREE(line);

753 754 755
    return 0;

error:
756 757
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
758
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
759
    if (temp_file)
760 761
        unlink(temp_file);
    VIR_FREE(temp_file);
762 763 764
    return -1;
}

765
int
766 767
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
768 769
    char *conf_file;
    int ret;
770

771
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
772 773
        return -1;

774 775 776
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
777 778
}

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

794 795
    fp = fopen(conf_file, "r");
    if (fp == NULL)
796 797
        return -1;

798
    VIR_FREE(*value);
799 800 801 802 803 804
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

805 806 807 808 809 810
        if (! STREQLEN(line, param, strlen(param)))
            continue;

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

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

825
    return err ? -1 : *value ? 1 : 0;
826 827
}

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

845
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
846 847
        return -1;

848
    ret = openvzReadConfigParam(conf_file, param, value);
849 850
    VIR_FREE(conf_file);
    return ret;
851 852 853 854 855
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
856 857 858 859
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
860 861
    int bytes_read;

862 863
    fp = fopen(from_path, "r");
    if (fp == NULL)
864 865 866
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
867
        VIR_FORCE_FCLOSE(fp);
868 869 870
        return -1;
    }

E
Eric Blake 已提交
871
    while (1) {
872
        if (getline(&line, &line_size, fp) <= 0)
873 874 875 876 877 878 879
            break;

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

880
    if (VIR_FCLOSE(fp) < 0)
881
        goto error;
882
    if (VIR_CLOSE(copy_fd) < 0)
883 884
        goto error;

885 886
    VIR_FREE(line);

887 888 889
    return 0;

error:
890 891
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
892
    VIR_FORCE_CLOSE(copy_fd);
893 894 895 896 897 898 899 900 901 902 903
    return -1;
}

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

910
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
911 912 913 914 915 916
        goto cleanup;

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

917 918
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
919
        virReportOOMError();
920
        goto cleanup;
921
    }
922

923
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
924 925 926 927 928 929 930 931 932
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
933
    VIR_FREE(configfile_value);
934
    VIR_FREE(conf_file);
935 936 937
    return ret;
}

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

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

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

    VIR_FREE(confdir);
    return ret;
}

961 962
static char *
openvzLocateConfDir(void)
963 964 965 966
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
967 968
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
969
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
970
        i++;
971 972 973 974 975 976
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
977
int
978
openvz_readline(int fd, char *ptr, int maxlen)
979 980 981 982
{
    int n, rc;
    char c;

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

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

1013
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1014
        return -1;
1015

1016 1017
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1018
        goto cleanup;
1019

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

1030 1031 1032 1033
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

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

C
Chris Lalancette 已提交
1048
    return retval;
1049 1050 1051 1052 1053
}

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

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

1065
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1066
        return -1;
1067

1068
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1069
        goto cleanup;
1070

J
Jim Meyering 已提交
1071
    if (uuidstr[0] == 0) {
1072
        fp = fopen(conf_file, "a"); /* append */
1073
        if (fp == NULL)
1074
            goto cleanup;
1075

1076 1077
        virUUIDFormat(uuid, uuidstr);

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

1085 1086
    ret = 0;
cleanup:
1087
    VIR_FORCE_FCLOSE(fp);
1088 1089
    VIR_FREE(conf_file);
    return ret;
1090 1091
}

1092 1093 1094 1095
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1096
    if (virUUIDGenerate(uuid)) {
1097 1098
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
1099 1100
        return -1;
    }
1101 1102 1103 1104

    return openvzSetDefinedUUID(vpsid, uuid);
}

1105 1106 1107 1108 1109 1110 1111
/*
 * 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.
 *
 */

1112
static int openvzAssignUUIDs(void)
1113 1114 1115 1116
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1117 1118 1119
    int vpsid;
    char *ext;
    int ret = 0;
1120 1121

    conf_dir = openvzLocateConfDir();
1122 1123
    if (conf_dir == NULL)
        return -1;
1124 1125

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1126
    if (dp == NULL) {
1127
        VIR_FREE(conf_dir);
1128 1129 1130
        return 0;
    }

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

1147
    closedir(dp);
1148
    VIR_FREE(conf_dir);
1149
    return ret;
1150
}
1151 1152 1153 1154 1155 1156 1157 1158


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1159 1160
    virCommandPtr cmd;
    char *outbuf;
1161
    char *temp;
1162
    int veid;
1163
    bool ok;
1164

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

E
Eric Blake 已提交
1173
    virCommandFree(cmd);
1174
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1175
    VIR_FREE(outbuf);
1176

1177 1178 1179
    if (ok && veid >= 0)
        return veid;

1180 1181
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1182 1183
    return -1;
}