openvz_conf.c 29.9 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 182
virCapsPtr openvzCapsInit(void)
{
    virCapsPtr caps;
    virCapsGuestPtr guest;

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

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

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

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

207
    caps->defaultConsoleTargetType = openvzDefaultConsoleType;
208 209

    return caps;
210
no_memory:
211
    virObjectUnref(caps);
212 213 214 215
    return NULL;
}


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

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

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

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

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

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

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

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

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

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

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

346 347 348 349
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

350 351
    VIR_FREE(temp);

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


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

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

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

385
    virBufferAdd(&buf, str_start, -1);
386

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

    return virBufferContentAndReset(&buf);
}


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

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

428
        if (VIR_ALLOC(fs) < 0)
429 430
            goto no_memory;

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

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

437
        VIR_FREE(veid_str);
438 439
    }

440 441
    fs->dst = strdup("/");

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

463 464 465 466 467 468 469 470
    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;

471 472
    VIR_FREE(temp);

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


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

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

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


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

559
    virObjectUnref(driver->xmlopt);
560
    virObjectUnref(driver->domains);
561
    virObjectUnref(driver->caps);
562
    VIR_FREE(driver);
563
}
D
Daniel Veillard 已提交
564 565 566



567
int openvzLoadDomains(struct openvz_driver *driver) {
568
    int veid, ret;
569
    char *status;
570
    char uuidstr[VIR_UUID_STRING_BUFLEN];
571
    virDomainObjPtr dom = NULL;
572
    virDomainDefPtr def = 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
    line = outbuf;
    while (line[0] != '\0') {
588
        unsigned int flags = 0;
589 590 591
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
592 593
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Failed to parse vzlist output"));
594
            goto cleanup;
595
        }
596
        *line++ = '\0';
597

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

601
        def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
602

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

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

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

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

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

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

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

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

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

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

        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;
664
        }
665 666
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
667

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

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

677
    return 0;
678

679
 no_memory:
680
    virReportOOMError();
681

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

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

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

    return nodeinfo.cpus;
}
701

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

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

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

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

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

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

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

749 750
    VIR_FREE(line);

751 752 753
    return 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

883 884
    VIR_FREE(line);

885 886 887
    return 0;

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

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

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

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

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

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

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

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

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

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

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

    VIR_FREE(confdir);
    return ret;
}

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

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

    return NULL;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1074 1075
        virUUIDFormat(uuid, uuidstr);

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

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

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

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

    return openvzSetDefinedUUID(vpsid, uuid);
}

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

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

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

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

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

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


/*
 * Return CTID from name
 *
 */

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

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

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

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

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