openvz_conf.c 29.8 KB
Newer Older
1 2 3
/*
 * openvz_conf.c: config functions for managing OpenVZ VEs
 *
4
 * Copyright (C) 2010-2012, 2014 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
 */

24
#include <config.h>
J
Jim Meyering 已提交
25

26 27 28 29 30
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
31
#include <sys/stat.h>
32
#include <sys/wait.h>
33

34
#include "virerror.h"
35
#include "openvz_conf.h"
36
#include "openvz_util.h"
37
#include "viruuid.h"
38
#include "virbuffer.h"
39
#include "viralloc.h"
E
Eric Blake 已提交
40
#include "virfile.h"
41
#include "vircommand.h"
42
#include "virstring.h"
43
#include "virhostcpu.h"
44

45 46
#define VIR_FROM_THIS VIR_FROM_OPENVZ

47
static char *openvzLocateConfDir(void);
48
static int openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len);
49
static int openvzAssignUUIDs(void);
50 51 52
static int openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext);

openvzLocateConfFileFunc openvzLocateConfFile = openvzLocateConfFileDefault;
53

54
int
55
strtoI(const char *str)
56 57 58
{
    int val;

59
    if (virStrToLong_i(str, NULL, 10, &val) < 0)
E
Eric Blake 已提交
60
        return 0;
61

62 63 64
    return val;
}

65 66

static int
67
openvzExtractVersionInfo(const char *cmdstr, int *retversion)
68
{
69
    int ret = -1;
70
    unsigned long version;
71
    char *help = NULL;
72
    char *tmp;
73
    virCommandPtr cmd = virCommandNewArgList(cmdstr, "--help", NULL);
74 75 76 77

    if (retversion)
        *retversion = 0;

78 79
    virCommandAddEnvString(cmd, "LC_ALL=C");
    virCommandSetOutputBuffer(cmd, &help);
80

81 82
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
83

84 85 86 87
    tmp = help;

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

I
Ilja Livenson 已提交
90
    if (virParseVersionString(tmp, &version, true) < 0)
91
        goto cleanup;
92 93 94 95 96 97

    if (retversion)
        *retversion = version;

    ret = 0;

98
 cleanup:
99
    virCommandFree(cmd);
100 101 102 103 104
    VIR_FREE(help);

    return ret;
}

105
int openvzExtractVersion(struct openvz_driver *driver)
106 107 108 109 110
{
    if (driver->version > 0)
        return 0;

    if (openvzExtractVersionInfo(VZCTL, &driver->version) < 0) {
111 112
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Could not extract vzctl version"));
113 114 115 116 117 118 119
        return -1;
    }

    return 0;
}


120 121 122 123 124 125
/* 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)
{
126 127
    char **tmp = NULL;
    size_t ntmp = 0;
P
Pavel Hrdina 已提交
128
    int ret = -1;
129

130
    if (!(tmp = virStringSplitCount(value, ":", 0, &ntmp)))
131 132
        goto error;

133
    if (ntmp != 2)
134
        goto error;
135 136

    if (barrier && virStrToLong_ull(tmp[0], NULL, 10, barrier) < 0)
137
        goto error;
138 139 140 141

    if (limit && virStrToLong_ull(tmp[1], NULL, 10, limit) < 0)
        goto error;

P
Pavel Hrdina 已提交
142
    ret = 0;
143
 error:
144
    virStringListFreeCount(tmp, ntmp);
P
Pavel Hrdina 已提交
145
    return ret;
146 147 148
}


149 150 151 152 153
virCapsPtr openvzCapsInit(void)
{
    virCapsPtr caps;
    virCapsGuestPtr guest;

154
    if ((caps = virCapabilitiesNew(virArchFromHost(),
155
                                   false, false)) == NULL)
156 157
        goto no_memory;

M
Martin Kletzander 已提交
158
    if (virCapabilitiesInitNUMA(caps) < 0)
159 160
        goto no_memory;

161 162 163
    if (virCapabilitiesInitCaches(caps) < 0)
        goto no_memory;

164
    if ((guest = virCapabilitiesAddGuest(caps,
165
                                         VIR_DOMAIN_OSTYPE_EXE,
166
                                         caps->host.arch,
167 168 169 170 171 172 173
                                         NULL,
                                         NULL,
                                         0,
                                         NULL)) == NULL)
        goto no_memory;

    if (virCapabilitiesAddGuestDomain(guest,
174
                                      VIR_DOMAIN_VIRT_OPENVZ,
175 176 177 178 179 180
                                      NULL,
                                      NULL,
                                      0,
                                      NULL) == NULL)
        goto no_memory;

181
    return caps;
182

183
 no_memory:
184
    virObjectUnref(caps);
185 186 187 188
    return NULL;
}


189
int
190
openvzReadNetworkConf(virDomainDefPtr def,
191 192
                      int veid)
{
193
    int ret;
194
    virDomainNetDefPtr net = NULL;
195
    char *temp = NULL;
196 197 198 199 200
    char *token, *saveptr = NULL;

    /*parse routing network configuration*
     * Sample from config:
     *   IP_ADDRESS="1.1.1.1 1.1.1.2"
N
Nehal J Wani 已提交
201
     * IPs split by space
202
     */
203
    ret = openvzReadVPSConfigParam(veid, "IP_ADDRESS", &temp);
204
    if (ret < 0) {
205 206 207
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'IP_ADDRESS' from config for container %d"),
                       veid);
208 209 210 211
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, " ", &saveptr);
        while (token != NULL) {
212
            if (VIR_ALLOC(net) < 0)
213
                goto error;
214 215

            net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
216
            if (virDomainNetAppendIPAddress(net, token, AF_UNSPEC, 0) < 0)
217
                goto error;
218

219
            if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0)
220
                goto error;
221

222 223 224 225 226 227
            token = strtok_r(NULL, " ", &saveptr);
        }
    }

    /*parse bridge devices*/
    /*Sample from config:
N
Nehal J Wani 已提交
228 229
     * NETIF="ifname=eth10,mac=00:18:51:C1:05:EE,host_ifname=veth105.10,host_mac=00:18:51:8F:D9:F3"
     *devices split by ';'
230
     */
231
    ret = openvzReadVPSConfigParam(veid, "NETIF", &temp);
232
    if (ret < 0) {
233 234 235
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'NETIF' from config for container %d"),
                       veid);
236 237 238 239 240
        goto error;
    } else if (ret > 0) {
        token = strtok_r(temp, ";", &saveptr);
        while (token != NULL) {
            /*add new device to list*/
241
            if (VIR_ALLOC(net) < 0)
242
                goto error;
243 244 245

            net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;

246
            char *p = token;
247 248 249 250 251
            char cpy_temp[32];
            int len;

            /*parse string*/
            do {
252
                char *next = strchrnul(p, ',');
253
                if (STRPREFIX(p, "ifname=")) {
254 255 256
                    /* skip in libvirt */
                } else if (STRPREFIX(p, "host_ifname=")) {
                    p += 12;
257 258
                    len = next - p;
                    if (len > 16) {
259 260
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long network device name"));
261 262 263
                        goto error;
                    }

264
                    if (VIR_ALLOC_N(net->ifname, len+1) < 0)
265
                        goto error;
266

267
                    if (virStrncpy(net->ifname, p, len, len+1) < 0) {
268 269
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Network ifname %s too long for destination"), p);
C
Chris Lalancette 已提交
270 271
                        goto error;
                    }
272 273 274 275
                } else if (STRPREFIX(p, "bridge=")) {
                    p += 7;
                    len = next - p;
                    if (len > 16) {
276 277
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Too long bridge device name"));
278 279 280
                        goto error;
                    }

281
                    if (VIR_ALLOC_N(net->data.bridge.brname, len+1) < 0)
282
                        goto error;
283

284
                    if (virStrncpy(net->data.bridge.brname, p, len, len+1) < 0) {
285 286
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("Bridge name %s too long for destination"), p);
C
Chris Lalancette 已提交
287 288
                        goto error;
                    }
289 290 291
                } else if (STRPREFIX(p, "mac=")) {
                    p += 4;
                    len = next - p;
292
                    if (len != 17) { /* should be 17 */
293 294
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong length MAC address"));
295 296
                        goto error;
                    }
297
                    if (virStrncpy(cpy_temp, p, len, sizeof(cpy_temp)) < 0) {
298 299
                        virReportError(VIR_ERR_INTERNAL_ERROR,
                                       _("MAC address %s too long for destination"), p);
C
Chris Lalancette 已提交
300 301
                        goto error;
                    }
302
                    if (virMacAddrParse(cpy_temp, &net->mac) < 0) {
303 304
                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                                       _("Wrong MAC address"));
305 306 307 308 309 310
                        goto error;
                    }
                }
                p = ++next;
            } while (p < token + strlen(token));

311
            if (VIR_APPEND_ELEMENT_COPY(def->nets, def->nnets, net) < 0)
312
                goto error;
313

314 315 316 317
            token = strtok_r(NULL, ";", &saveptr);
        }
    }

318 319
    VIR_FREE(temp);

320
    return 0;
321

322
 error:
323
    VIR_FREE(temp);
324
    virDomainNetDefFree(net);
325
    return -1;
326 327 328
}


329
static int
330
openvzReadFSConf(virDomainDefPtr def,
331 332
                 int veid)
{
333 334
    int ret;
    virDomainFSDefPtr fs = NULL;
335 336
    char *veid_str = NULL;
    char *temp = NULL;
337 338
    const char *param;
    unsigned long long barrier, limit;
339

340
    ret = openvzReadVPSConfigParam(veid, "OSTEMPLATE", &temp);
341
    if (ret < 0) {
342 343 344
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read 'OSTEMPLATE' from config for container %d"),
                       veid);
345 346
        goto error;
    } else if (ret > 0) {
347
        if (!(fs = virDomainFSDefNew()))
348
            goto error;
349 350

        fs->type = VIR_DOMAIN_FS_TYPE_TEMPLATE;
351
        fs->src->path = g_strdup(temp);
352 353
    } else {
        /* OSTEMPLATE was not found, VE was booted from a private dir directly */
354
        ret = openvzReadVPSConfigParam(veid, "VE_PRIVATE", &temp);
355
        if (ret <= 0) {
356 357 358
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read 'VE_PRIVATE' from config for container %d"),
                           veid);
359 360
            goto error;
        }
361

362
        if (VIR_ALLOC(fs) < 0)
363
            goto error;
364

365
        veid_str = g_strdup_printf("%d", veid);
366 367

        fs->type = VIR_DOMAIN_FS_TYPE_MOUNT;
368
        if (!(fs->src->path = virStringReplace(temp, "$VEID", veid_str)))
369
            goto error;
370

371
        VIR_FREE(veid_str);
372 373
    }

374
    fs->dst = g_strdup("/");
375

376 377 378 379
    param = "DISKSPACE";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret > 0) {
        if (openvzParseBarrierLimit(temp, &barrier, &limit)) {
380 381 382
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read '%s' from config for container %d"),
                           param, veid);
383 384 385
            goto error;
        } else {
            /* Ensure that we can multiply by 1024 without overflowing. */
386
            if (barrier > ULLONG_MAX / 1024 ||
387
                limit > ULLONG_MAX / 1024) {
J
Jiri Denemark 已提交
388 389
                virReportError(VIR_ERR_OVERFLOW, "%s",
                               _("Unable to parse quota"));
390 391 392 393 394 395 396
                goto error;
            }
            fs->space_soft_limit = barrier * 1024; /* unit is bytes */
            fs->space_hard_limit = limit * 1024;   /* unit is bytes */
        }
    }

397
    if (VIR_APPEND_ELEMENT(def->fss, def->nfss, fs) < 0)
398
        goto error;
399

400 401
    VIR_FREE(temp);

402
    return 0;
403
 error:
404
    VIR_FREE(temp);
405 406 407 408 409
    virDomainFSDefFree(fs);
    return -1;
}


410 411 412
static int
openvzReadMemConf(virDomainDefPtr def, int veid)
{
413
    int ret = -1;
414 415 416
    char *temp = NULL;
    unsigned long long barrier, limit;
    const char *param;
417
    long kb_per_pages;
418

419 420
    kb_per_pages = openvzKBPerPages();
    if (kb_per_pages < 0)
421 422 423 424 425 426
        goto error;

    /* Memory allocation guarantee */
    param = "VMGUARPAGES";
    ret = openvzReadVPSConfigParam(veid, param, &temp);
    if (ret < 0) {
427 428 429
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
430 431 432 433
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, NULL);
        if (ret < 0) {
434 435 436
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier of '%s' "
                             "from config for container %d"), param, veid);
437 438 439 440 441 442 443 444 445 446 447 448
            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) {
449 450 451
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not read '%s' from config for container %d"),
                       param, veid);
452 453 454 455
        goto error;
    } else if (ret > 0) {
        ret = openvzParseBarrierLimit(temp, &barrier, &limit);
        if (ret < 0) {
456 457 458
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not parse barrier and limit of '%s' "
                             "from config for container %d"), param, veid);
459 460 461
            goto error;
        }
        if (barrier == LONG_MAX)
462
            def->mem.soft_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
463 464 465 466
        else
            def->mem.soft_limit = barrier * kb_per_pages;

        if (limit == LONG_MAX)
467
            def->mem.hard_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
468 469 470 471 472
        else
            def->mem.hard_limit = limit * kb_per_pages;
    }

    ret = 0;
473
 error:
474 475 476 477 478
    VIR_FREE(temp);
    return ret;
}


479 480 481 482 483 484
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    if (!driver)
        return;
485

486
    virObjectUnref(driver->xmlopt);
487
    virObjectUnref(driver->domains);
488
    virObjectUnref(driver->caps);
489
    VIR_FREE(driver);
490
}
D
Daniel Veillard 已提交
491 492 493



494 495
int openvzLoadDomains(struct openvz_driver *driver)
{
496
    int veid, ret;
497
    char *status;
498
    char uuidstr[VIR_UUID_STRING_BUFLEN];
499
    virDomainObjPtr dom = NULL;
500
    virDomainDefPtr def = NULL;
501
    char *temp = NULL;
E
Eric Blake 已提交
502 503 504
    char *outbuf = NULL;
    char *line;
    virCommandPtr cmd = NULL;
505
    unsigned int vcpus = 0;
506

507 508
    if (openvzAssignUUIDs() < 0)
        return -1;
509

E
Eric Blake 已提交
510 511 512 513
    cmd = virCommandNewArgList(VZLIST, "-a", "-ovpsid,status", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;
514

515 516
    line = outbuf;
    while (line[0] != '\0') {
517
        unsigned int flags = 0;
518 519 520
        if (virStrToLong_i(line, &status, 10, &veid) < 0 ||
            *status++ != ' ' ||
            (line = strchr(status, '\n')) == NULL) {
521 522
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Failed to parse vzlist output"));
523
            goto cleanup;
524
        }
525
        *line++ = '\0';
526

527
        if (!(def = virDomainDefNew()))
528
            goto cleanup;
529

530
        def->virtType = VIR_DOMAIN_VIRT_OPENVZ;
531

532 533
        if (STREQ(status, "stopped"))
            def->id = -1;
J
Jiri Denemark 已提交
534
        else
535
            def->id = veid;
536
        def->name = g_strdup_printf("%i", veid);
537

538
        openvzGetVPSUUID(veid, uuidstr, sizeof(uuidstr));
539
        ret = virUUIDParse(uuidstr, def->uuid);
540

541
        if (ret == -1) {
542 543
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("UUID in config file malformed"));
544
            goto cleanup;
545
        }
546

547
        def->os.type = VIR_DOMAIN_OSTYPE_EXE;
548
        def->os.init = g_strdup("/sbin/init");
549

550
        ret = openvzReadVPSConfigParam(veid, "CPUS", &temp);
551
        if (ret < 0) {
552 553 554
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Could not read config for container %d"),
                           veid);
555
            goto cleanup;
556
        } else if (ret > 0) {
557
            vcpus = strtoI(temp);
558 559
        }

560
        if (ret == 0 || vcpus == 0)
561
            vcpus = virHostCPUGetCount();
562

563
        if (virDomainDefSetVcpusMax(def, vcpus, driver->xmlopt) < 0)
564 565
            goto cleanup;

566 567
        if (virDomainDefSetVcpus(def, vcpus) < 0)
            goto cleanup;
568

569
        /* XXX load rest of VM config data .... */
570

571 572 573
        openvzReadNetworkConf(def, veid);
        openvzReadFSConf(def, veid);
        openvzReadMemConf(def, veid);
574

575
        virUUIDFormat(def->uuid, uuidstr);
576 577 578 579
        flags = VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE;
        if (STRNEQ(status, "stopped"))
            flags |= VIR_DOMAIN_OBJ_LIST_ADD_LIVE;

580 581
        if (!(dom = virDomainObjListAdd(driver->domains,
                                        def,
582
                                        driver->xmlopt,
583 584
                                        flags,
                                        NULL)))
585
            goto cleanup;
586 587 588 589 590 591 592 593 594

        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;
595
        }
596 597
        /* XXX OpenVZ doesn't appear to have concept of a transient domain */
        dom->persistent = 1;
598

599
        virDomainObjEndAPI(&dom);
600
        dom = NULL;
601
        def = NULL;
602
    }
603

E
Eric Blake 已提交
604
    virCommandFree(cmd);
605
    VIR_FREE(temp);
E
Eric Blake 已提交
606
    VIR_FREE(outbuf);
607

608
    return 0;
609

610
 cleanup:
E
Eric Blake 已提交
611
    virCommandFree(cmd);
612
    VIR_FREE(temp);
E
Eric Blake 已提交
613
    VIR_FREE(outbuf);
614
    virDomainDefFree(def);
615
    return -1;
616 617
}

618 619
static int
openvzWriteConfigParam(const char * conf_file, const char *param, const char *value)
620
{
621
    char * temp_file = NULL;
622 623 624 625
    int temp_fd = -1;
    FILE *fp;
    char *line = NULL;
    size_t line_size = 0;
626

627
    temp_file = g_strdup_printf("%s.tmp", conf_file);
628

629 630
    fp = fopen(conf_file, "r");
    if (fp == NULL)
631
        goto error;
632
    temp_fd = open(temp_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
633
    if (temp_fd == -1)
634
        goto error;
635

E
Eric Blake 已提交
636
    while (1) {
637
        if (getline(&line, &line_size, fp) <= 0)
638 639
            break;

640
        if (!(STRPREFIX(line, param) && line[strlen(param)] == '=')) {
641 642 643 644 645 646 647 648 649 650 651 652
            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;

653
    if (VIR_FCLOSE(fp) < 0)
654
        goto error;
655
    if (VIR_CLOSE(temp_fd) < 0)
656 657 658 659 660
        goto error;

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

661 662
    VIR_FREE(line);

663 664
    return 0;

665
 error:
666 667
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
668
    VIR_FORCE_CLOSE(temp_fd);
E
Eric Blake 已提交
669
    if (temp_file)
670 671
        unlink(temp_file);
    VIR_FREE(temp_file);
672 673 674
    return -1;
}

675
int
676 677
openvzWriteVPSConfigParam(int vpsid, const char *param, const char *value)
{
678 679
    char *conf_file;
    int ret;
680

681
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
682 683
        return -1;

684 685 686
    ret = openvzWriteConfigParam(conf_file, param, value);
    VIR_FREE(conf_file);
    return ret;
687 688
}

689 690 691
/*
 * value will be freed before a new value is assigned to it, the caller is
 * responsible for freeing it afterwards.
692 693
 *
 * Returns <0 on error, 0 if not found, 1 if found.
694
 */
695
int
696
openvzReadConfigParam(const char *conf_file, const char *param, char **value)
697
{
698 699 700
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
701 702
    int err = 0;
    char *sf, *token, *saveptr = NULL;
703

704 705
    fp = fopen(conf_file, "r");
    if (fp == NULL)
706 707
        return -1;

708
    VIR_FREE(*value);
709 710 711 712 713 714
    while (1) {
        if (getline(&line, &line_size, fp) < 0) {
            err = !feof(fp);
            break;
        }

J
Ján Tomko 已提交
715
        if (!(sf = STRSKIP(line, param)))
716 717
            continue;

J
Ján Tomko 已提交
718 719
        if (*sf++ != '=')
            continue;
720

721
        saveptr = NULL;
722 723
        if ((token = strtok_r(sf, "\"\t\n", &saveptr)) != NULL) {
            VIR_FREE(*value);
724
            *value = g_strdup(token);
725 726
            /* keep going - last entry wins */
        }
727
    }
728 729
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
730

731
    return err ? -1 : *value ? 1 : 0;
732 733
}

734
/*
735 736
 * Read parameter from container config
 *
N
Nitesh Konkar 已提交
737
 * value will be freed before a new value is assigned to it, the caller is
738 739 740 741 742 743 744
 * responsible for freeing it afterwards.
 *
 * sample: 133, "OSTEMPLATE", &value
 * return: -1 - error
 *          0 - don't found
 *          1 - OK
 */
745
int
746
openvzReadVPSConfigParam(int vpsid, const char *param, char **value)
747
{
748 749
    char *conf_file;
    int ret;
750

751
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
752 753
        return -1;

754
    ret = openvzReadConfigParam(conf_file, param, value);
755 756
    VIR_FREE(conf_file);
    return ret;
757 758 759 760 761
}

static int
openvz_copyfile(char* from_path, char* to_path)
{
762 763 764 765
    char *line = NULL;
    size_t line_size = 0;
    FILE *fp;
    int copy_fd;
766 767
    int bytes_read;

768 769
    fp = fopen(from_path, "r");
    if (fp == NULL)
770 771 772
        return -1;
    copy_fd = open(to_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (copy_fd == -1) {
773
        VIR_FORCE_FCLOSE(fp);
774 775 776
        return -1;
    }

E
Eric Blake 已提交
777
    while (1) {
778
        if (getline(&line, &line_size, fp) <= 0)
779 780 781 782 783 784 785
            break;

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

786
    if (VIR_FCLOSE(fp) < 0)
787
        goto error;
788
    if (VIR_CLOSE(copy_fd) < 0)
789 790
        goto error;

791 792
    VIR_FREE(line);

793 794
    return 0;

795
 error:
796 797
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
798
    VIR_FORCE_CLOSE(copy_fd);
799 800 801 802 803 804 805 806 807 808 809
    return -1;
}

/*
* Copy the default config to the VE conf file
* return: -1 - error
*          0 - OK
*/
int
openvzCopyDefaultConfig(int vpsid)
{
810 811 812
    char *confdir = NULL;
    char *default_conf_file = NULL;
    char *configfile_value = NULL;
813
    char *conf_file = NULL;
814 815
    int ret = -1;

816
    if (openvzReadConfigParam(VZ_CONF_FILE, "CONFIGFILE", &configfile_value) < 0)
817 818 819 820 821 822
        goto cleanup;

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

823 824
    default_conf_file = g_strdup_printf("%s/ve-%s.conf-sample", confdir,
                                        configfile_value);
825

826
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
827 828 829 830 831 832
        goto cleanup;

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

    ret = 0;
833
 cleanup:
834 835
    VIR_FREE(confdir);
    VIR_FREE(default_conf_file);
836
    VIR_FREE(configfile_value);
837
    VIR_FREE(conf_file);
838 839 840
    return ret;
}

841
/* Locate config file of container
842 843
 * return -1 - error
 *         0 - OK */
844
static int
845
openvzLocateConfFileDefault(int vpsid, char **conffile, const char *ext)
846
{
847
    char *confdir;
848 849 850 851 852 853
    int ret = 0;

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

854
    *conffile = g_strdup_printf("%s/%d.%s", confdir, vpsid, ext ? ext : "conf");
855 856 857 858 859

    VIR_FREE(confdir);
    return ret;
}

860 861
static char *
openvzLocateConfDir(void)
862 863
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
864
    size_t i = 0;
865
    char *ret = NULL;
866

E
Eric Blake 已提交
867
    while (conf_dir_list[i]) {
868
        if (virFileExists(conf_dir_list[i])) {
869
            ret = g_strdup(conf_dir_list[i]);
870 871
            goto cleanup;
        }
E
Eric Blake 已提交
872
        i++;
873 874
    }

875
 cleanup:
876
    return ret;
877 878 879
}

/* Richard Steven's classic readline() function */
880
int
881
openvz_readline(int fd, char *ptr, int maxlen)
882 883 884 885
{
    int n, rc;
    char c;

E
Eric Blake 已提交
886
    for (n = 1; n < maxlen; n++) {
887
        if ((rc = read(fd, &c, 1)) == 1) {
888
            *ptr++ = c;
E
Eric Blake 已提交
889
            if (c == '\n')
890
                break;
E
Eric Blake 已提交
891 892
        } else if (rc == 0) {
            if (n == 1)
893 894 895 896 897 898 899 900 901 902 903
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

904
static int
905
openvzGetVPSUUID(int vpsid, char *uuidstr, size_t len)
906
{
907
    char *conf_file;
908 909
    char *line = NULL;
    size_t line_size = 0;
910
    char *saveptr = NULL;
911 912
    char *uuidbuf;
    char *iden;
913
    FILE *fp;
914
    int retval = -1;
915

916
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
917
        return -1;
918

919 920
    fp = fopen(conf_file, "r");
    if (fp == NULL)
921
        goto cleanup;
922

E
Eric Blake 已提交
923
    while (1) {
924 925 926 927 928 929 930
        if (getline(&line, &line_size, fp) < 0) {
            if (feof(fp)) { /* EOF, UUID was not found */
                uuidstr[0] = 0;
                break;
            } else {
                goto cleanup;
            }
931 932
        }

933 934 935 936
        iden = strtok_r(line, " ", &saveptr);
        uuidbuf = strtok_r(NULL, "\n", &saveptr);

        if (iden != NULL && uuidbuf != NULL && STREQ(iden, "#UUID:")) {
937
            if (virStrcpy(uuidstr, uuidbuf, len) < 0) {
938 939
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("invalid uuid %s"), uuidbuf);
940 941
                goto cleanup;
            }
942 943 944
            break;
        }
    }
945
    retval = 0;
946
 cleanup:
947 948
    VIR_FREE(line);
    VIR_FORCE_FCLOSE(fp);
949
    VIR_FREE(conf_file);
950

C
Chris Lalancette 已提交
951
    return retval;
952 953 954 955 956
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
957 958
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
959
{
960
    char *conf_file;
961
    char uuidstr[VIR_UUID_STRING_BUFLEN];
962
    FILE *fp = NULL;
963
    int ret = -1;
964 965 966

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

968
    if (openvzLocateConfFile(vpsid, &conf_file, "conf") < 0)
E
Eric Blake 已提交
969
        return -1;
970

971
    if (openvzGetVPSUUID(vpsid, uuidstr, sizeof(uuidstr)))
972
        goto cleanup;
973

J
Jim Meyering 已提交
974
    if (uuidstr[0] == 0) {
975
        fp = fopen(conf_file, "a"); /* append */
976
        if (fp == NULL)
977
            goto cleanup;
978

979 980
        virUUIDFormat(uuid, uuidstr);

981
        /* Record failure if fprintf or VIR_FCLOSE fails,
982
           and be careful always to close the stream.  */
983 984
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0) ||
            (VIR_FCLOSE(fp) == EOF))
985
            goto cleanup;
986 987
    }

988
    ret = 0;
989
 cleanup:
990
    VIR_FORCE_FCLOSE(fp);
991 992
    VIR_FREE(conf_file);
    return ret;
993 994
}

995
static int
996 997
openvzSetUUID(int vpsid)
{
998 999
    unsigned char uuid[VIR_UUID_BUFLEN];

1000
    if (virUUIDGenerate(uuid) < 0) {
1001 1002
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Failed to generate UUID"));
1003 1004
        return -1;
    }
1005 1006 1007 1008

    return openvzSetDefinedUUID(vpsid, uuid);
}

1009 1010 1011 1012 1013 1014 1015
/*
 * 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.
 *
 */

1016
static int openvzAssignUUIDs(void)
1017 1018 1019 1020
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
1021 1022 1023
    int vpsid;
    char *ext;
    int ret = 0;
1024 1025

    conf_dir = openvzLocateConfDir();
1026 1027
    if (conf_dir == NULL)
        return -1;
1028

J
Ján Tomko 已提交
1029
    if (virDirOpenQuiet(&dp, conf_dir) < 0) {
1030
        VIR_FREE(conf_dir);
1031 1032 1033
        return 0;
    }

E
Eric Blake 已提交
1034
    while ((ret = virDirRead(dp, &dent, conf_dir)) > 0) {
1035 1036 1037
        if (virStrToLong_i(dent->d_name, &ext, 10, &vpsid) < 0 ||
            *ext++ != '.' ||
            STRNEQ(ext, "conf"))
1038
            continue;
E
Eric Blake 已提交
1039
        if (vpsid > 0) /* '0.conf' belongs to the host, ignore it */
1040 1041
            openvzSetUUID(vpsid);
    }
1042

J
Ján Tomko 已提交
1043
    VIR_DIR_CLOSE(dp);
1044
    VIR_FREE(conf_dir);
1045
    return ret;
1046
}
1047 1048 1049 1050 1051 1052 1053


/*
 * Return CTID from name
 *
 */

1054 1055
int openvzGetVEID(const char *name)
{
E
Eric Blake 已提交
1056 1057
    virCommandPtr cmd;
    char *outbuf;
1058
    char *temp;
1059
    int veid;
1060
    bool ok;
1061

E
Eric Blake 已提交
1062 1063 1064 1065 1066
    cmd = virCommandNewArgList(VZLIST, name, "-ovpsid", "-H", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0) {
        virCommandFree(cmd);
        VIR_FREE(outbuf);
1067 1068 1069
        return -1;
    }

E
Eric Blake 已提交
1070
    virCommandFree(cmd);
1071
    ok = virStrToLong_i(outbuf, &temp, 10, &veid) == 0 && *temp == '\n';
E
Eric Blake 已提交
1072
    VIR_FREE(outbuf);
1073

1074 1075 1076
    if (ok && veid >= 0)
        return veid;

1077 1078
    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                   _("Failed to parse vzlist output"));
1079 1080
    return -1;
}
1081 1082 1083 1084


static int
openvzDomainDefPostParse(virDomainDefPtr def,
1085
                         virCapsPtr caps,
1086 1087 1088 1089
                         unsigned int parseFlags G_GNUC_UNUSED,
                         void *opaque G_GNUC_UNUSED,
                         void *parseOpaque G_GNUC_UNUSED)
{
1090 1091 1092 1093 1094
    if (!virCapabilitiesDomainSupported(caps, def->os.type,
                                        def->os.arch,
                                        def->virtType))
        return -1;

1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
    /* fill the init path */
    if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init)
        def->os.init = g_strdup("/sbin/init");

    return 0;
}


static int
openvzDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
                               const virDomainDef *def G_GNUC_UNUSED,
                               virCapsPtr caps G_GNUC_UNUSED,
                               unsigned int parseFlags G_GNUC_UNUSED,
                               void *opaque G_GNUC_UNUSED,
                               void *parseOpaque G_GNUC_UNUSED)
{
    if (dev->type == VIR_DOMAIN_DEVICE_CHR &&
        dev->data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
        dev->data.chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)
        dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ;

    /* forbid capabilities mode hostdev in this kind of hypervisor */
    if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
        dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("hostdev mode 'capabilities' is not "
                         "supported in %s"),
                       virDomainVirtTypeToString(def->virtType));
        return -1;
    }

    return 0;
}


virDomainDefParserConfig openvzDomainDefParserConfig = {
    .domainPostParseCallback = openvzDomainDefPostParse,
    .devicesPostParseCallback = openvzDomainDeviceDefPostParse,
    .features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
};

virDomainXMLOptionPtr openvzXMLOption(void)
{
    return virDomainXMLOptionNew(&openvzDomainDefParserConfig,
                                 NULL, NULL, NULL, NULL);
}