openvz_conf.c 29.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 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
23
 * Authors:
24 25 26
 * Shuveb Hussain <shuveb@binarykarma.com>
 * Anoop Joe Cyriac <anoop@binarykarma.com>
 *
27 28
 */

29
#include <config.h>
J
Jim Meyering 已提交
30

31 32 33 34 35 36 37
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
38 39 40 41
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
D
Daniel P. Berrange 已提交
42
#include <string.h>
43
#include <sys/utsname.h>
44
#include <sys/wait.h>
45

46
#include "virterror_internal.h"
47
#include "openvz_conf.h"
48
#include "openvz_util.h"
49 50
#include "uuid.h"
#include "buf.h"
51
#include "memory.h"
52
#include "util.h"
53
#include "nodeinfo.h"
E
Eric Blake 已提交
54
#include "virfile.h"
E
Eric Blake 已提交
55
#include "command.h"
56
#include "ignore-value.h"
57

58 59
#define VIR_FROM_THIS VIR_FROM_OPENVZ

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

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
66

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

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

75 76 77
    return val;
}

78 79

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

    if (retversion)
        *retversion = 0;

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

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

97 98 99 100
    tmp = help;

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

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

    if (retversion)
        *retversion = version;

    ret = 0;

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

    return ret;
}

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

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

    return 0;
}


133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* Parse config values of the form barrier:limit into barrier and limit */
static int
openvzParseBarrierLimit(const char* value,
                        unsigned long long *barrier,
                        unsigned long long *limit)
{
    char *token;
    char *saveptr = NULL;
    char *str = strdup(value);

    if (str == NULL) {
        virReportOOMError();
        goto error;
    }

    token = strtok_r(str, ":", &saveptr);
    if (token == NULL) {
        goto error;
    } else {
        if (barrier != NULL) {
            if (virStrToLong_ull(token, NULL, 10, barrier))
                goto error;
        }
    }
    token = strtok_r(NULL, ":", &saveptr);
    if (token == NULL) {
        goto error;
    } else {
        if (limit != NULL) {
            if (virStrToLong_ull(token, NULL, 10, limit))
                goto error;
        }
    }
    return 0;
error:
    VIR_FREE(str);
    return -1;
}


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

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

    uname(&utsname);

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

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

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

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

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

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

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


223
int
224
openvzReadNetworkConf(virDomainDefPtr def,
225
                      int veid) {
226
    int ret;
227
    virDomainNetDefPtr net = NULL;
228
    char *temp = NULL;
229 230 231 232 233 234 235
    char *token, *saveptr = NULL;

    /*parse routing network configuration*
     * Sample from config:
     *   IP_ADDRESS="1.1.1.1 1.1.1.2"
     *   splited IPs by space
     */
236
    ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
237
    if (ret < 0) {
238 239 240
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read 'IP_ADDRESS' from config for container %d"),
                    veid);
241 242 243 244
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, " ", &saveptr);
        while (token != NULL) {
245
            if (VIR_ALLOC(net) < 0)
246 247 248 249 250 251 252 253
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
            net->data.ethernet.ipaddr = strdup(token);

            if (net->data.ethernet.ipaddr == NULL)
                goto no_memory;

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

259 260 261 262 263 264 265 266 267
            token = strtok_r(NULL, " ", &saveptr);
        }
    }

    /*parse bridge devices*/
    /*Sample from config:
     *NETIF="ifname=eth10,mac=00:18:51:C1:05:EE,host_ifname=veth105.10,host_mac=00:18:51:8F:D9:F3"
     *devices splited by ';'
     */
268
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
269
    if (ret < 0) {
270 271 272
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read 'NETIF' from config for container %d"),
                    veid);
273 274 275 276 277
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
            /*add new device to list*/
278
            if (VIR_ALLOC(net) < 0)
279 280 281 282
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

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

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

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

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

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

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

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

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

357 358
    VIR_FREE(temp);

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


369 370 371 372 373 374 375
/* utility function to replace 'from' by 'to' in 'str' */
static char*
openvz_replace(const char* str,
               const char* from,
               const char* to) {
    const char* offset = NULL;
    const char* str_start = str;
376 377
    int to_len;
    int from_len;
378 379
    virBuffer buf = VIR_BUFFER_INITIALIZER;

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

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

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

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

    return virBufferContentAndReset(&buf);
}


403
static int
404
openvzReadFSConf(virDomainDefPtr def,
405 406 407
                 int veid) {
    int ret;
    virDomainFSDefPtr fs = NULL;
408 409
    char *veid_str = NULL;
    char *temp = NULL;
410

411
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
412
    if (ret < 0) {
413
        openvzError(VIR_ERR_INTERNAL_ERROR,
414
                    _("Could not read 'OSTEMPLATE' from config for container %d"),
415 416 417 418 419 420 421 422
                    veid);
        goto error;
    } else if (ret > 0) {
        if (VIR_ALLOC(fs) < 0)
            goto no_memory;

        fs->type = VIR_DOMAIN_FS_TYPE_TEMPLATE;
        fs->src = strdup(temp);
423 424
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
425
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
426
        if (ret <= 0) {
427
            openvzError(VIR_ERR_INTERNAL_ERROR,
428
                        _("Could not read 'VE_PRIVATE' from config for container %d"),
429 430 431
                        veid);
            goto error;
        }
432

433
        if (VIR_ALLOC(fs) < 0)
434 435
            goto no_memory;

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

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

442
        VIR_FREE(veid_str);
443 444
    }

445 446 447 448 449 450 451 452 453 454
    fs->dst = strdup("/");

    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;

455 456
    VIR_FREE(temp);

457 458
    return 0;
no_memory:
459
    virReportOOMError();
460
error:
461
    VIR_FREE(temp);
462 463 464 465 466
    virDomainFSDefFree(fs);
    return -1;
}


467 468 469 470 471 472 473
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
    int ret;
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
474
    long kb_per_pages;
475

476 477
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
        goto error;

    /* Memory allocation guarantee */
    param = "VMGUARPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read '%s' from config for container %d"),
                    param, veid);
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
        if (ret < 0) {
            openvzError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse  barrier of '%s' "
                          "from config for container %d"), param, veid);
            goto error;
        }
        if (barrier == LONG_MAX)
            def->mem.min_guarantee = 0ull;
        else
            def->mem.min_guarantee = barrier * kb_per_pages;
    }

    /* Memory hard and soft limits */
    param = "PRIVVMPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
        openvzError(VIR_ERR_INTERNAL_ERROR,
                    _("Could not read '%s' from config for container %d"),
                    param, veid);
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
        if (ret < 0) {
            openvzError(VIR_ERR_INTERNAL_ERROR,
                        _("Could not parse barrier and limit of '%s' "
                          "from config for container %d"), param, veid);
            goto error;
        }
        if (barrier == LONG_MAX)
            def->mem.soft_limit = 0ull;
        else
            def->mem.soft_limit = barrier * kb_per_pages;

        if (limit == LONG_MAX)
            def->mem.hard_limit = 0ull;
        else
            def->mem.hard_limit = limit * kb_per_pages;
    }

    ret = 0;
error:
    VIR_FREE(temp);
    return ret;
}


536 537 538 539 540 541
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
542

543
    virDomainObjListDeinit(&driver->domains);
544
    virCapabilitiesFree(driver->caps);
545
    VIR_FREE(driver);
546
}
D
Daniel Veillard 已提交
547 548 549



550
int openvzLoadDomains(struct openvz_driver *driver) {
551
    int veid, ret;
552
    char *status;
553
    char uuidstr[VIR_UUID_STRING_BUFLEN];
554
    virDomainObjPtr dom = NULL;
555
    char *temp = NULL;
E
Eric Blake 已提交
556 557 558
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
559

560 561
    if (openvzAssignUUIDs() < 0)
        return -1;
562

E
Eric Blake 已提交
563 564 565 566
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
567

568 569 570 571 572
    line = outbuf;
    while (line[0] != '\0') {
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
573 574
            openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("Failed to parse vzlist output"));
575
            goto cleanup;
576
        }
577
        *line++ = '\0';
578

579
        if (VIR_ALLOC(dom) < 0)
580
            goto no_memory;
581

582
        if (virMutexInit(&dom->lock) < 0) {
583 584
            openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("cannot initialize mutex"));
585 586 587 588
            VIR_FREE(dom);
            goto cleanup;
        }

589 590
        virDomainObjLock(dom);

591 592
        if (VIR_ALLOC(dom->def) < 0)
            goto no_memory;
593

594 595
        dom->def->virtType = VIR_DOMAIN_VIRT_OPENVZ;

J
Jiri Denemark 已提交
596 597 598 599 600 601 602
        if (STREQ(status, "stopped")) {
            virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF,
                                 VIR_DOMAIN_SHUTOFF_UNKNOWN);
        } else {
            virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
                                 VIR_DOMAIN_RUNNING_UNKNOWN);
        }
603

604
        dom->refs = 1;
605
        dom->pid = veid;
J
Jiri Denemark 已提交
606 607 608 609
        if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_SHUTOFF)
            dom->def->id = -1;
        else
            dom->def->id = veid;
610 611
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
612

613
        if (virAsprintf(&dom->def->name, "%i", veid) < 0)
614
            goto no_memory;
615

616
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
617
        ret = virUUIDParse(uuidstr, dom->def->uuid);
618

619
        if (ret == -1) {
620 621
            openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("UUID in config file malformed"));
622
            goto cleanup;
623
        }
624

625 626 627 628
        if (!(dom->def->os.type = strdup("exe")))
            goto no_memory;
        if (!(dom->def->os.init = strdup("/sbin/init")))
            goto no_memory;
629

630
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
631
        if (ret < 0) {
632
            openvzError(VIR_ERR_INTERNAL_ERROR,
633
                        _("Could not read config for container %d"),
634 635
                        veid);
            goto cleanup;
636
        } else if (ret > 0) {
E
Eric Blake 已提交
637
            dom->def->maxvcpus = strtoI(temp);
638 639
        }

E
Eric Blake 已提交
640 641 642
        if (ret == 0 || dom->def->maxvcpus == 0)
            dom->def->maxvcpus = openvzGetNodeCPUs();
        dom->def->vcpus = dom->def->maxvcpus;
643

644
        /* XXX load rest of VM config data .... */
645

646 647
        openvzReadNetworkConf(dom->def, veid);
        openvzReadFSConf(dom->def, veid);
648
        openvzReadMemConf(dom->def, veid);
649

650 651
        virUUIDFormat(dom->def->uuid, uuidstr);
        if (virHashAddEntry(driver->domains.objs, uuidstr, dom) < 0)
652
            goto cleanup;
653

654
        virDomainObjUnlock(dom);
655
        dom = NULL;
656
    }
657

E
Eric Blake 已提交
658
    virCommandFree(cmd);
659
    VIR_FREE(temp);
E
Eric Blake 已提交
660
    VIR_FREE(outbuf);
661

662
    return 0;
663

664
 no_memory:
665
    virReportOOMError();
666

667
 cleanup:
E
Eric Blake 已提交
668
    virCommandFree(cmd);
669
    VIR_FREE(temp);
E
Eric Blake 已提交
670
    VIR_FREE(outbuf);
671
    /* dom hasn't been shared yet, so unref should return 0 */
672
    if (dom)
673
        ignore_value(virDomainObjUnref(dom));
674
    return -1;
675 676
}

677 678 679 680 681
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

682
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
683 684 685 686
        return 0;

    return nodeinfo.cpus;
}
687

688 689
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
690
{
691
    char * temp_file = NULL;
692 693 694 695
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
696

697
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
698
        virReportOOMError();
699
        return -1;
700
    }
701

702 703
    fp = fopen(conf_file, "r");
    if (fp == NULL)
704
        goto error;
705 706
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
707
        goto error;
708 709
    }

E
Eric Blake 已提交
710
    while (1) {
711
        if (getline(&line, &line_size, fp) <= 0)
712 713
            break;

714
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
715 716 717 718 719 720 721 722 723 724 725 726
            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;

727
    if (VIR_FCLOSE(fp) < 0)
728
        goto error;
729
    if (VIR_CLOSE(temp_fd) < 0)
730 731 732 733 734
        goto error;

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

735 736
    VIR_FREE(line);

737 738 739
    return 0;

error:
740 741
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
742
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
743
    if (temp_file)
744 745
        unlink(temp_file);
    VIR_FREE(temp_file);
746 747 748
    return -1;
}

749
int
750 751
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
752 753
    char *conf_file;
    int ret;
754

755
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
756 757
        return -1;

758 759 760
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
761 762
}

763 764 765
/*
 * value will be freed before a new value is assigned to it, the caller is
 * responsible for freeing it afterwards.
766 767
 *
 * Returns <0 on error, 0 if not found, 1 if found.
768
 */
769
int
770
openvzReadConfigParam(const char *conf_file, const char *param, char **value)
771
{
772 773 774
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
775 776
    int err = 0;
    char *sf, *token, *saveptr = NULL;
777

778 779
    fp = fopen(conf_file, "r");
    if (fp == NULL)
780 781
        return -1;

782
    VIR_FREE(*value);
783 784 785 786 787 788
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

789 790 791 792 793 794
        if (! STREQLEN(line, param, strlen(param)))
            continue;

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

795
        saveptr = NULL;
796 797 798 799 800 801
        if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
            VIR_FREE(*value);
            *value = strdup(token);
            if (*value == NULL) {
                err = 1;
                break;
802
            }
803 804
            /* keep going - last entry wins */
        }
805
    }
806 807
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
808

809
    return err ? -1 : *value ? 1 : 0;
810 811
}

812
/*
813 814 815 816 817 818 819 820 821 822
 * 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
 */
823
int
824
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
825
{
826 827
    char *conf_file;
    int ret;
828

829
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
830 831
        return -1;

832
    ret = openvzReadConfigParam(conf_file, param, value);
833 834
    VIR_FREE(conf_file);
    return ret;
835 836 837 838 839
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
840 841 842 843
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
844 845
    int bytes_read;

846 847
    fp = fopen(from_path, "r");
    if (fp == NULL)
848 849 850
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
851
        VIR_FORCE_FCLOSE(fp);
852 853 854
        return -1;
    }

E
Eric Blake 已提交
855
    while (1) {
856
        if (getline(&line, &line_size, fp) <= 0)
857 858 859 860 861 862 863
            break;

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

864
    if (VIR_FCLOSE(fp) < 0)
865
        goto error;
866
    if (VIR_CLOSE(copy_fd) < 0)
867 868
        goto error;

869 870
    VIR_FREE(line);

871 872 873
    return 0;

error:
874 875
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
876
    VIR_FORCE_CLOSE(copy_fd);
877 878 879 880 881 882 883 884 885 886 887
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
888 889 890
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
891
    char *conf_file = NULL;
892 893
    int ret = -1;

894
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
895 896 897 898 899 900
        goto cleanup;

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

901 902
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
903
        virReportOOMError();
904
        goto cleanup;
905
    }
906

907
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
908 909 910 911 912 913 914 915 916
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
917
    VIR_FREE(configfile_value);
918
    VIR_FREE(conf_file);
919 920 921
    return ret;
}

922
/* Locate config file of container
923 924
 * return -1 - error
 *         0 - OK */
925
static int
926
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
927
{
928
    char *confdir;
929 930 931 932 933 934
    int ret = 0;

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

935 936 937
    if (virAsprintf(conffile, "%s/%d.%s", confdir, vpsid,
                    ext ? ext : "conf") < 0) {
        virReportOOMError();
938
        ret = -1;
939
    }
940 941 942 943 944

    VIR_FREE(confdir);
    return ret;
}

945 946
static char *
openvzLocateConfDir(void)
947 948 949 950
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
951 952
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
953
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
954
        i++;
955 956 957 958 959 960
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
961
int
962
openvz_readline(int fd, char *ptr, int maxlen)
963 964 965 966
{
    int n, rc;
    char c;

E
Eric Blake 已提交
967 968
    for (n = 1; n < maxlen; n++) {
        if ( (rc = read(fd, &c, 1)) == 1) {
969
            *ptr++ = c;
E
Eric Blake 已提交
970
            if (c == '\n')
971
                break;
E
Eric Blake 已提交
972 973
        } else if (rc == 0) {
            if (n == 1)
974 975 976 977 978 979 980 981 982 983 984
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

985
static int
986
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
987
{
988
    char *conf_file;
989 990
    char *line = NULL;
    size_t line_size = 0;
991
    char *saveptr = NULL;
992 993
    char *uuidbuf;
    char *iden;
994
    FILE *fp;
995
    int retval = -1;
996

997
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
998
        return -1;
999

1000 1001
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1002
        goto cleanup;
1003

E
Eric Blake 已提交
1004
    while (1) {
1005 1006 1007 1008 1009 1010 1011
        if (getline(&line, &line_size, fp) < 0) {
            if (feof(fp)) { /* EOF, UUID was not found */
                uuidstr[0] = 0;
                break;
            } else {
                goto cleanup;
            }
1012 1013
        }

1014 1015 1016 1017
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
1018 1019 1020 1021 1022
            if (virStrcpy(uuidstr, uuidbuf, len) == NULL) {
                openvzError(VIR_ERR_INTERNAL_ERROR,
                            _("invalid uuid %s"), uuidbuf);
                goto cleanup;
            }
1023 1024 1025
            break;
        }
    }
1026 1027
    retval = 0;
cleanup:
1028 1029
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
1030
    VIR_FREE(conf_file);
1031

C
Chris Lalancette 已提交
1032
    return retval;
1033 1034 1035 1036 1037
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
1038 1039
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
1040
{
1041
    char *conf_file;
1042
    char uuidstr[VIR_UUID_STRING_BUFLEN];
1043
    FILE *fp = NULL;
1044
    int ret = -1;
1045 1046 1047

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

1049
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1050
        return -1;
1051

1052
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1053
        goto cleanup;
1054

J
Jim Meyering 已提交
1055
    if (uuidstr[0] == 0) {
1056
        fp = fopen(conf_file, "a"); /* append */
1057
        if (fp == NULL)
1058
            goto cleanup;
1059

1060 1061
        virUUIDFormat(uuid, uuidstr);

1062
        /* Record failure if fprintf or VIR_FCLOSE fails,
1063
           and be careful always to close the stream.  */
1064 1065
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
            (VIR_FCLOSE(fp) == EOF))
1066
            goto cleanup;
1067 1068
    }

1069 1070
    ret = 0;
cleanup:
1071
    VIR_FORCE_FCLOSE(fp);
1072 1073
    VIR_FREE(conf_file);
    return ret;
1074 1075
}

1076 1077 1078 1079
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1080
    if (virUUIDGenerate(uuid)) {
1081 1082
        openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("Failed to generate UUID"));
1083 1084
        return -1;
    }
1085 1086 1087 1088

    return openvzSetDefinedUUID(vpsid, uuid);
}

1089 1090 1091 1092 1093 1094 1095
/*
 * 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.
 *
 */

1096
static int openvzAssignUUIDs(void)
1097 1098 1099 1100
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1101 1102 1103
    int vpsid;
    char *ext;
    int ret = 0;
1104 1105

    conf_dir = openvzLocateConfDir();
1106 1107
    if (conf_dir == NULL)
        return -1;
1108 1109

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1110
    if (dp == NULL) {
1111
        VIR_FREE(conf_dir);
1112 1113 1114
        return 0;
    }

1115
    errno = 0;
E
Eric Blake 已提交
1116
    while ((dent = readdir(dp))) {
1117 1118 1119
        if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
            *ext++ != '.' ||
            STRNEQ(ext, "conf"))
1120
            continue;
E
Eric Blake 已提交
1121
        if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
1122
            openvzSetUUID(vpsid);
1123 1124 1125 1126 1127 1128
        errno = 0;
    }
    if (errno) {
        openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                    _("Failed to scan configuration directory"));
        ret = -1;
1129
    }
1130

1131
    closedir(dp);
1132
    VIR_FREE(conf_dir);
1133
    return ret;
1134
}
1135 1136 1137 1138 1139 1140 1141 1142


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1143 1144
    virCommandPtr cmd;
    char *outbuf;
1145
    char *temp;
1146
    int veid;
1147
    bool ok;
1148

E
Eric Blake 已提交
1149 1150 1151 1152 1153
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1154 1155 1156
        return -1;
    }

E
Eric Blake 已提交
1157
    virCommandFree(cmd);
1158
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1159
    VIR_FREE(outbuf);
1160

1161 1162 1163
    if (ok && veid >= 0)
        return veid;

1164 1165
    openvzError(VIR_ERR_INTERNAL_ERROR, "%s",
                _("Failed to parse vzlist output"));
1166 1167
    return -1;
}