openvz_conf.c 29.7 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
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
D
Daniel P. Berrange 已提交
41
#include <string.h>
42
#include <sys/wait.h>
43

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

55 56
#define VIR_FROM_THIS VIR_FROM_OPENVZ

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

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
63

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

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

72 73 74
    return val;
}

75 76

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

    if (retversion)
        *retversion = 0;

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

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

94 95 96 97
    tmp = help;

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

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

    if (retversion)
        *retversion = version;

    ret = 0;

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

    return ret;
}

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

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

    return 0;
}


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


170 171 172 173 174
virCapsPtr openvzCapsInit(void)
{
    virCapsPtr caps;
    virCapsGuestPtr guest;

175
    if ((caps = virCapabilitiesNew(virArchFromHost(),
176 177 178
                                   0, 0)) == NULL)
        goto no_memory;

179
    if (nodeCapsInitNUMA(caps) < 0)
180 181
        goto no_memory;

182 183
    if ((guest = virCapabilitiesAddGuest(caps,
                                         "exe",
184
                                         caps->host.arch,
185 186 187 188 189 190 191 192 193 194 195 196 197 198
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
        goto no_memory;

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

199
    return caps;
200

201
no_memory:
202
    virObjectUnref(caps);
203 204 205 206
    return NULL;
}


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

238 239 240 241 242
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

243 244 245 246 247 248 249 250 251
            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 ';'
     */
252
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
253
    if (ret < 0) {
254 255 256
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'NETIF' from config for container %d"),
                       veid);
257 258 259 260 261
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
            /*add new device to list*/
262
            if (VIR_ALLOC(net) < 0)
263 264 265 266
                goto no_memory;

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

267
            char *p = token;
268 269 270 271 272
            char cpy_temp[32];
            int len;

            /*parse string*/
            do {
273
                char *next = strchrnul(p, ',');
274
                if (STRPREFIX(p, "ifname=")) {
275 276 277
                    /* skip in libvirt */
                } else if (STRPREFIX(p, "host_ifname=")) {
                    p += 12;
278 279
                    len = next - p;
                    if (len > 16) {
280 281
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long network device name"));
282 283 284
                        goto error;
                    }

285 286 287
                    if (VIR_ALLOC_N(net->ifname, len+1) < 0)
                        goto no_memory;

C
Chris Lalancette 已提交
288
                    if (virStrncpy(net->ifname, p, len, len+1) == NULL) {
289 290
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Network ifname %s too long for destination"), p);
C
Chris Lalancette 已提交
291 292
                        goto error;
                    }
293 294 295 296
                } else if (STRPREFIX(p, "bridge=")) {
                    p += 7;
                    len = next - p;
                    if (len > 16) {
297 298
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long bridge device name"));
299 300 301
                        goto error;
                    }

302 303 304
                    if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
                        goto no_memory;

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

332 333 334 335 336
            if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
                goto no_memory;
            def->nets[def->nnets++] = net;
            net = NULL;

337 338 339 340
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

341 342
    VIR_FREE(temp);

343
    return 0;
344
no_memory:
345
    virReportOOMError();
346
error:
347
    VIR_FREE(temp);
348
    virDomainNetDefFree(net);
349
    return -1;
350 351 352
}


353 354 355 356 357 358 359
/* 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;
360 361
    int to_len;
    int from_len;
362 363
    virBuffer buf = VIR_BUFFER_INITIALIZER;

364
    if ((!from) || (!to))
365
        return NULL;
366 367
    from_len = strlen(from);
    to_len = strlen(to);
368

E
Eric Blake 已提交
369
    while ((offset = strstr(str_start, from)))
370 371 372 373 374 375
    {
        virBufferAdd(&buf, str_start, offset-str_start);
        virBufferAdd(&buf, to, to_len);
        str_start = offset + from_len;
    }

376
    virBufferAdd(&buf, str_start, -1);
377

378 379 380 381
    if (virBufferError(&buf)) {
        virBufferFreeAndReset(&buf);
        return NULL;
    }
382 383 384 385 386

    return virBufferContentAndReset(&buf);
}


387
static int
388
openvzReadFSConf(virDomainDefPtr def,
389 390 391
                 int veid) {
    int ret;
    virDomainFSDefPtr fs = NULL;
392 393
    char *veid_str = NULL;
    char *temp = NULL;
394 395
    const char *param;
    unsigned long long barrier, limit;
396

397
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
398
    if (ret < 0) {
399 400 401
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'OSTEMPLATE' from config for container %d"),
                       veid);
402 403 404 405 406 407 408
        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);
409 410
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
411
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
412
        if (ret <= 0) {
413 414 415
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read 'VE_PRIVATE' from config for container %d"),
                           veid);
416 417
            goto error;
        }
418

419
        if (VIR_ALLOC(fs) < 0)
420 421
            goto no_memory;

422 423
        if (virAsprintf(&veid_str, "%d", veid) < 0)
            goto no_memory;
424 425 426

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

428
        VIR_FREE(veid_str);
429 430
    }

431 432
    fs->dst = strdup("/");

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

454 455 456 457 458 459 460 461
    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;

462 463
    VIR_FREE(temp);

464 465
    return 0;
no_memory:
466
    virReportOOMError();
467
error:
468
    VIR_FREE(temp);
469 470 471 472 473
    virDomainFSDefFree(fs);
    return -1;
}


474 475 476
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
477
    int ret = -1;
478 479 480
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
481
    long kb_per_pages;
482

483 484
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
485 486 487 488 489 490
        goto error;

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


543 544 545 546 547 548
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
549

550
    virObjectUnref(driver->xmlopt);
551
    virObjectUnref(driver->domains);
552
    virObjectUnref(driver->caps);
553
    VIR_FREE(driver);
554
}
D
Daniel Veillard 已提交
555 556 557



558
int openvzLoadDomains(struct openvz_driver *driver) {
559
    int veid, ret;
560
    char *status;
561
    char uuidstr[VIR_UUID_STRING_BUFLEN];
562
    virDomainObjPtr dom = NULL;
563
    virDomainDefPtr def = NULL;
564
    char *temp = NULL;
E
Eric Blake 已提交
565 566 567
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
568

569 570
    if (openvzAssignUUIDs() < 0)
        return -1;
571

E
Eric Blake 已提交
572 573 574 575
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
576

577 578
    line = outbuf;
    while (line[0] != '\0') {
579
        unsigned int flags = 0;
580 581 582
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
583 584
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Failed to parse vzlist output"));
585
            goto cleanup;
586
        }
587
        *line++ = '\0';
588

589
        if (VIR_ALLOC(def) < 0)
590
            goto no_memory;
591

592
        def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
593

594 595
        if (STREQ(status, "stopped"))
            def->id = -1;
J
Jiri Denemark 已提交
596
        else
597 598
            def->id = veid;
        if (virAsprintf(&def->name, "%i", veid) < 0)
599
            goto no_memory;
600

601
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
602
        ret = virUUIDParse(uuidstr, def->uuid);
603

604
        if (ret == -1) {
605 606
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
607
            goto cleanup;
608
        }
609

610
        if (!(def->os.type = strdup("exe")))
611
            goto no_memory;
612
        if (!(def->os.init = strdup("/sbin/init")))
613
            goto no_memory;
614

615
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
616
        if (ret < 0) {
617 618 619
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
620
            goto cleanup;
621
        } else if (ret > 0) {
622
            def->maxvcpus = strtoI(temp);
623 624
        }

625 626 627
        if (ret == 0 || def->maxvcpus == 0)
            def->maxvcpus = openvzGetNodeCPUs();
        def->vcpus = def->maxvcpus;
628

629
        /* XXX load rest of VM config data .... */
630

631 632 633
        openvzReadNetworkConf(def, veid);
        openvzReadFSConf(def, veid);
        openvzReadMemConf(def, veid);
634

635
        virUUIDFormat(def->uuid, uuidstr);
636 637 638 639
        flags = VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE;
        if (STRNEQ(status, "stopped"))
            flags |= VIR_DOMAIN_OBJ_LIST_ADD_LIVE;

640 641
        if (!(dom = virDomainObjListAdd(driver->domains,
                                        def,
642
                                        driver->xmlopt,
643 644
                                        flags,
                                        NULL)))
645
            goto cleanup;
646 647 648 649 650 651 652 653 654

        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;
655
        }
656 657
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
658

659
        virObjectUnlock(dom);
660
        dom = NULL;
661
        def = NULL;
662
    }
663

E
Eric Blake 已提交
664
    virCommandFree(cmd);
665
    VIR_FREE(temp);
E
Eric Blake 已提交
666
    VIR_FREE(outbuf);
667

668
    return 0;
669

670
 no_memory:
671
    virReportOOMError();
672

673
 cleanup:
E
Eric Blake 已提交
674
    virCommandFree(cmd);
675
    VIR_FREE(temp);
E
Eric Blake 已提交
676
    VIR_FREE(outbuf);
677
    virObjectUnref(dom);
678
    virDomainDefFree(def);
679
    return -1;
680 681
}

682 683 684 685 686
unsigned int
openvzGetNodeCPUs(void)
{
    virNodeInfo nodeinfo;

687
    if (nodeGetInfo(NULL, &nodeinfo) < 0)
688 689 690 691
        return 0;

    return nodeinfo.cpus;
}
692

693 694
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
695
{
696
    char * temp_file = NULL;
697 698 699 700
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
701

702
    if (virAsprintf(&temp_file, "%s.tmp", conf_file)<0) {
703
        virReportOOMError();
704
        return -1;
705
    }
706

707 708
    fp = fopen(conf_file, "r");
    if (fp == NULL)
709
        goto error;
710 711
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (temp_fd == -1) {
712
        goto error;
713 714
    }

E
Eric Blake 已提交
715
    while (1) {
716
        if (getline(&line, &line_size, fp) <= 0)
717 718
            break;

719
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
720 721 722 723 724 725 726 727 728 729 730 731
            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;

732
    if (VIR_FCLOSE(fp) < 0)
733
        goto error;
734
    if (VIR_CLOSE(temp_fd) < 0)
735 736 737 738 739
        goto error;

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

740 741
    VIR_FREE(line);

742 743 744
    return 0;

error:
745 746
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
747
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
748
    if (temp_file)
749 750
        unlink(temp_file);
    VIR_FREE(temp_file);
751 752 753
    return -1;
}

754
int
755 756
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
757 758
    char *conf_file;
    int ret;
759

760
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
761 762
        return -1;

763 764 765
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
766 767
}

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

783 784
    fp = fopen(conf_file, "r");
    if (fp == NULL)
785 786
        return -1;

787
    VIR_FREE(*value);
788 789 790 791 792 793
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

794 795 796 797 798 799
        if (! STREQLEN(line, param, strlen(param)))
            continue;

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

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

814
    return err ? -1 : *value ? 1 : 0;
815 816
}

817
/*
818 819 820 821 822 823 824 825 826 827
 * 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
 */
828
int
829
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
830
{
831 832
    char *conf_file;
    int ret;
833

834
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
835 836
        return -1;

837
    ret = openvzReadConfigParam(conf_file, param, value);
838 839
    VIR_FREE(conf_file);
    return ret;
840 841 842 843 844
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
845 846 847 848
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
849 850
    int bytes_read;

851 852
    fp = fopen(from_path, "r");
    if (fp == NULL)
853 854 855
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
856
        VIR_FORCE_FCLOSE(fp);
857 858 859
        return -1;
    }

E
Eric Blake 已提交
860
    while (1) {
861
        if (getline(&line, &line_size, fp) <= 0)
862 863 864 865 866 867 868
            break;

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

869
    if (VIR_FCLOSE(fp) < 0)
870
        goto error;
871
    if (VIR_CLOSE(copy_fd) < 0)
872 873
        goto error;

874 875
    VIR_FREE(line);

876 877 878
    return 0;

error:
879 880
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
881
    VIR_FORCE_CLOSE(copy_fd);
882 883 884 885 886 887 888 889 890 891 892
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
893 894 895
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
896
    char *conf_file = NULL;
897 898
    int ret = -1;

899
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
900 901 902 903 904 905
        goto cleanup;

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

906 907
    if (virAsprintf(&default_conf_file, "%s/ve-%s.conf-sample", confdir,
                    configfile_value) < 0) {
908
        virReportOOMError();
909
        goto cleanup;
910
    }
911

912
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
913 914 915 916 917 918 919 920 921
        goto cleanup;

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

    ret = 0;
cleanup:
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
922
    VIR_FREE(configfile_value);
923
    VIR_FREE(conf_file);
924 925 926
    return ret;
}

927
/* Locate config file of container
928 929
 * return -1 - error
 *         0 - OK */
930
static int
931
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
932
{
933
    char *confdir;
934 935 936 937 938 939
    int ret = 0;

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

940 941 942
    if (virAsprintf(conffile, "%s/%d.%s", confdir, vpsid,
                    ext ? ext : "conf") < 0) {
        virReportOOMError();
943
        ret = -1;
944
    }
945 946 947 948 949

    VIR_FREE(confdir);
    return ret;
}

950 951
static char *
openvzLocateConfDir(void)
952 953 954 955
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

E
Eric Blake 已提交
956 957
    while (conf_dir_list[i]) {
        if (!access(conf_dir_list[i], F_OK))
958
            return strdup(conf_dir_list[i]);
E
Eric Blake 已提交
959
        i++;
960 961 962 963 964 965
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
966
int
967
openvz_readline(int fd, char *ptr, int maxlen)
968 969 970 971
{
    int n, rc;
    char c;

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

990
static int
991
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
992
{
993
    char *conf_file;
994 995
    char *line = NULL;
    size_t line_size = 0;
996
    char *saveptr = NULL;
997 998
    char *uuidbuf;
    char *iden;
999
    FILE *fp;
1000
    int retval = -1;
1001

1002
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1003
        return -1;
1004

1005 1006
    fp = fopen(conf_file, "r");
    if (fp == NULL)
1007
        goto cleanup;
1008

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

1019 1020 1021 1022
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
1023
            if (virStrcpy(uuidstr, uuidbuf, len) == NULL) {
1024 1025
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("invalid uuid %s"), uuidbuf);
1026 1027
                goto cleanup;
            }
1028 1029 1030
            break;
        }
    }
1031 1032
    retval = 0;
cleanup:
1033 1034
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
1035
    VIR_FREE(conf_file);
1036

C
Chris Lalancette 已提交
1037
    return retval;
1038 1039 1040 1041 1042
}

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

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

1054
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
1055
        return -1;
1056

1057
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
1058
        goto cleanup;
1059

J
Jim Meyering 已提交
1060
    if (uuidstr[0] == 0) {
1061
        fp = fopen(conf_file, "a"); /* append */
1062
        if (fp == NULL)
1063
            goto cleanup;
1064

1065 1066
        virUUIDFormat(uuid, uuidstr);

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

1074 1075
    ret = 0;
cleanup:
1076
    VIR_FORCE_FCLOSE(fp);
1077 1078
    VIR_FREE(conf_file);
    return ret;
1079 1080
}

1081 1082 1083 1084
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

1085
    if (virUUIDGenerate(uuid)) {
1086 1087
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
1088 1089
        return -1;
    }
1090 1091 1092 1093

    return openvzSetDefinedUUID(vpsid, uuid);
}

1094 1095 1096 1097 1098 1099 1100
/*
 * 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.
 *
 */

1101
static int openvzAssignUUIDs(void)
1102 1103 1104 1105
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1106 1107 1108
    int vpsid;
    char *ext;
    int ret = 0;
1109 1110

    conf_dir = openvzLocateConfDir();
1111 1112
    if (conf_dir == NULL)
        return -1;
1113 1114

    dp = opendir(conf_dir);
E
Eric Blake 已提交
1115
    if (dp == NULL) {
1116
        VIR_FREE(conf_dir);
1117 1118 1119
        return 0;
    }

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

1136
    closedir(dp);
1137
    VIR_FREE(conf_dir);
1138
    return ret;
1139
}
1140 1141 1142 1143 1144 1145 1146 1147


/*
 * Return CTID from name
 *
 */

int openvzGetVEID(const char *name) {
E
Eric Blake 已提交
1148 1149
    virCommandPtr cmd;
    char *outbuf;
1150
    char *temp;
1151
    int veid;
1152
    bool ok;
1153

E
Eric Blake 已提交
1154 1155 1156 1157 1158
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1159 1160 1161
        return -1;
    }

E
Eric Blake 已提交
1162
    virCommandFree(cmd);
1163
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1164
    VIR_FREE(outbuf);
1165

1166 1167 1168
    if (ok && veid >= 0)
        return veid;

1169 1170
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1171 1172
    return -1;
}