openvz_conf.c 30.0 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
    virObjectUnref(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
    virDomainDefPtr def = NULL;
574
    char *temp = NULL;
E
Eric Blake 已提交
575 576 577
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
578

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

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

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

599
        if (VIR_ALLOC(def) < 0)
600
            goto no_memory;
601

602
        def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
603

604 605
        if (STREQ(status, "stopped"))
            def->id = -1;
J
Jiri Denemark 已提交
606
        else
607 608
            def->id = veid;
        if (virAsprintf(&def->name, "%i", veid) < 0)
609
            goto no_memory;
610

611
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
612
        ret = virUUIDParse(uuidstr, def->uuid);
613

614
        if (ret == -1) {
615 616
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
617
            goto cleanup;
618
        }
619

620
        if (!(def->os.type = strdup("exe")))
621
            goto no_memory;
622
        if (!(def->os.init = strdup("/sbin/init")))
623
            goto no_memory;
624

625
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
626
        if (ret < 0) {
627 628 629
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
630
            goto cleanup;
631
        } else if (ret > 0) {
632
            def->maxvcpus = strtoI(temp);
633 634
        }

635 636 637
        if (ret == 0 || def->maxvcpus == 0)
            def->maxvcpus = openvzGetNodeCPUs();
        def->vcpus = def->maxvcpus;
638

639
        /* XXX load rest of VM config data .... */
640

641 642 643
        openvzReadNetworkConf(def, veid);
        openvzReadFSConf(def, veid);
        openvzReadMemConf(def, veid);
644

645
        virUUIDFormat(def->uuid, uuidstr);
646 647 648 649
        flags = VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE;
        if (STRNEQ(status, "stopped"))
            flags |= VIR_DOMAIN_OBJ_LIST_ADD_LIVE;

650 651 652
        if (!(dom = virDomainObjListAdd(driver->domains,
                                        driver->caps,
                                        def,
653 654
                                        flags,
                                        NULL)))
655
            goto cleanup;
656 657 658 659 660 661 662 663 664

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

669
        virObjectUnlock(dom);
670
        dom = NULL;
671
        def = NULL;
672
    }
673

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

678
    return 0;
679

680
 no_memory:
681
    virReportOOMError();
682

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

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

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

    return nodeinfo.cpus;
}
702

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

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

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

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

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

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

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

750 751
    VIR_FREE(line);

752 753 754
    return 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

884 885
    VIR_FREE(line);

886 887 888
    return 0;

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

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

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

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

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

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

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

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

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

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

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

    VIR_FREE(confdir);
    return ret;
}

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

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

    return NULL;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1075 1076
        virUUIDFormat(uuid, uuidstr);

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

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

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

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

    return openvzSetDefinedUUID(vpsid, uuid);
}

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

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

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

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

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

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


/*
 * Return CTID from name
 *
 */

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

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

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

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

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