openvz_conf.c 20.6 KB
Newer Older
1 2 3 4 5
/*
 * openvz_conf.c: config functions for managing OpenVZ VEs
 *
 * Copyright (C) 2006, 2007 Binary Karma
 * Copyright (C) 2006 Shuveb Hussain
6
 * Copyright (C) 2007 Anoop Joe Cyriac
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
 *
22
 * Authors:
23 24 25
 * Shuveb Hussain <shuveb@binarykarma.com>
 * Anoop Joe Cyriac <anoop@binarykarma.com>
 *
26 27
 */

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

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 <strings.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 44 45 46 47 48

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/uri.h>

D
Daniel P. Berrange 已提交
49
#include "internal.h"
50 51

#include "openvz_driver.h"
52
#include "openvz_conf.h"
53 54
#include "uuid.h"
#include "buf.h"
55
#include "memory.h"
56
#include "util.h"
D
Daniel Veillard 已提交
57 58
#include "xml.h"
#include "domain_conf.h"
59

60 61 62 63
static char *openvzLocateConfDir(void);
static struct openvz_vm_def *openvzParseXML(virConnectPtr conn, xmlDocPtr xml);
static int openvzGetVPSUUID(int vpsid, char *uuidstr);
static int openvzSetUUID(int vpsid);
64
static int openvzLocateConfFile(int vpsid, char *conffile, int maxlen);
65

66
void
D
Daniel Veillard 已提交
67
openvzError (virConnectPtr conn, virErrorNumber code, const char *fmt, ...)
68
{
69 70
    va_list args;
    char errorMessage[OPENVZ_MAX_ERROR_LEN];
71 72
    const char *errmsg;

73 74 75 76 77 78 79 80 81
    if (fmt) {
        va_start(args, fmt);
        vsnprintf(errorMessage, OPENVZ_MAX_ERROR_LEN-1, fmt, args);
        va_end(args);
    } else {
        errorMessage[0] = '\0';
    }

    errmsg = __virErrorMsg(code, (errorMessage[0] ? errorMessage : NULL));
82
    __virRaiseError (conn, NULL, NULL, VIR_FROM_OPENVZ,
83 84
                     code, VIR_ERR_ERROR, errmsg, errorMessage, NULL, 0, 0,
                     errmsg, errorMessage);
85 86
}

87
struct openvz_vm
88
*openvzFindVMByID(const struct openvz_driver *driver, int id) {
89 90 91 92 93 94 95 96 97 98 99
    struct openvz_vm *vm = driver->vms;

    while (vm) {
        if (vm->vpsid == id)
            return vm;
        vm = vm->next;
    }

    return NULL;
}

100
struct openvz_vm
101
*openvzFindVMByUUID(const struct openvz_driver *driver,
102 103 104 105
                                   const unsigned char *uuid) {
    struct openvz_vm *vm = driver->vms;

    while (vm) {
106
        if (!memcmp(vm->vmdef->uuid, uuid, VIR_UUID_BUFLEN))
107 108 109 110 111 112 113
            return vm;
        vm = vm->next;
    }

    return NULL;
}

114
struct openvz_vm
115
*openvzFindVMByName(const struct openvz_driver *driver,
116 117 118 119
                                   const char *name) {
    struct  openvz_vm *vm = driver->vms;

    while (vm) {
120
        if (STREQ(vm->vmdef->name, name))
121 122 123 124 125 126 127
            return vm;
        vm = vm->next;
    }

    return NULL;
}

128
int
129
strtoI(const char *str)
130 131 132
{
    int val;

133 134
    if (virStrToLong_i(str, NULL, 10, &val) < 0)
        return 0 ;
135

136 137 138
    return val;
}

D
Daniel Veillard 已提交
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
/* function checks MAC address is empty
   return 0 - empty
          1 - not
*/
int openvzCheckEmptyMac(const unsigned char *mac)
{
    int i;
    for (i = 0; i < VIR_DOMAIN_NET_MAC_SIZE; i++)
        if (mac[i] != 0x00)
            return 1;

    return 0;
}

/* convert mac address to string
   return pointer to string or NULL
*/
char *openvzMacToString(const unsigned char *mac)
{
    char str[20];
    if (snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
                      mac[0], mac[1], mac[2],
                      mac[3], mac[4], mac[5]) >= 18)
        return NULL;

    return strdup(str);
}

167 168 169 170 171 172 173 174 175 176 177 178
void
openvzRemoveInactiveVM(struct openvz_driver *driver, struct openvz_vm *vm)
{
    driver->num_inactive--;
    openvzFreeVM(driver, vm, 1);
}

/* Free all memory associated with a openvz_vm_def structure */
void
openvzFreeVMDef(struct openvz_vm_def *def)
{
    if (def) {
D
Daniel Veillard 已提交
179
        virDomainNetDefFree(def->net);
180
    }
181 182
}

183
/* Free all memory associated with a openvz_vm structure
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
 * @checkCallee == 0 then openvzFreeDriver() is callee else some other function
 */
void
openvzFreeVM(struct openvz_driver *driver, struct openvz_vm *vm,
             int checkCallee)
{
    struct openvz_vm *vms;

    if (!vm && !driver)
        return;
    vms = driver->vms;
    if (checkCallee) {
        if (vms == vm)
            driver->vms = vm->next;
        else {
            while (vms) {
                struct openvz_vm *prev = vms;

                vms = vms->next;
                if (vms == vm) {
                    prev->next = vms->next;
                    break;
                }
            }
        }
209
    }
210 211
    if (vms) {
        openvzFreeVMDef(vm->vmdef);
212
        VIR_FREE(vm);
213
    }
214
}
215

216 217 218 219 220 221 222 223 224 225 226
/* Free all memory associated with a openvz_driver structure */
void
openvzFreeDriver(struct openvz_driver *driver)
{
    struct openvz_vm *next;

    if (!driver)
        return;
    if (driver->vms)
        for(next = driver->vms->next; driver->vms; driver->vms = next)
            openvzFreeVM(driver, driver->vms, 0);
227
    VIR_FREE(driver);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
}

struct openvz_vm *
openvzAssignVMDef(virConnectPtr conn,
                  struct openvz_driver *driver, struct openvz_vm_def *def)
{
    struct openvz_vm *vm = NULL;

    if (!driver || !def)
        return NULL;

    if ((vm = openvzFindVMByName(driver, def->name))) {
        if (!openvzIsActiveVM(vm)) {
            openvzFreeVMDef(vm->vmdef);
            vm->vmdef = def;
        }
        else
        {
246
            openvzLog(OPENVZ_ERR,
247 248
                      _("Error already an active OPENVZ VM having id '%s'"),
                      def->name);
249 250 251 252 253 254 255
            openvzFreeVMDef(def);
            return NULL; /* can't redefine an active domain */
        }

        return vm;
    }

256
    if (VIR_ALLOC(vm) < 0) {
257
        openvzFreeVMDef(def);
D
Daniel Veillard 已提交
258
        openvzError(conn, VIR_ERR_NO_MEMORY, _("vm"));
259 260 261 262 263 264 265 266 267 268 269 270 271 272
        return NULL;
    }

    vm->vpsid = -1;     /* -1 needed for to represent inactiveness of domain before 'start' */
    vm->status = VIR_DOMAIN_SHUTOFF;
    vm->vmdef = def;
    vm->next = driver->vms;

    driver->vms = vm;
    driver->num_inactive++;

    return vm;
}

273
struct openvz_vm_def
274 275 276 277 278 279
*openvzParseVMDef(virConnectPtr conn,
                 const char *xmlStr, const char *displayName)
{
    xmlDocPtr xml;
    struct openvz_vm_def *def = NULL;

280
    xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "domain.xml", NULL,
281 282
            XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
    if (!xml) {
D
Daniel Veillard 已提交
283
        openvzError(conn, VIR_ERR_XML_ERROR, NULL);
284 285 286 287 288 289 290
        return NULL;
    }

    def = openvzParseXML(conn, xml);
    xmlFreeDoc(xml);

    return def;
291 292
}

D
Daniel Veillard 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
/* Parse filesystem section
Sample:
<filesystem type="template">
      <source name="fedora-core-5-i386"/>
      <quota type="size" max="10000"/>
      <quota type="inodes" max="100"/>
</filesystem>
*/
static int openvzParseDomainFS(virConnectPtr conn,
                               struct openvz_fs_def *fs,
                               xmlXPathContextPtr ctxt)
{
    xmlNodePtr cur, obj;
    char *type = NULL;
    int n;
    xmlNodePtr *nodes = NULL;


311 312
    if ((n = virXPathNodeSet(conn, "/domain/devices/filesystem",
                             ctxt, &nodes)) < 0) {
D
Daniel Veillard 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
        openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                   _("missing filesystem tag"));
        goto error;
    }

    if (n > 1) {
        openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                   _("There should be only one filesystem tag"));
        goto error;
    }

    obj = nodes[0];

    /*check template type*/
    type = virXMLPropString(obj, "type");
    if (type == NULL) {
         openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                  _("missing type attribute"));
         goto error;
    }

    if (STRNEQ(type, "template")) {
         openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                 _("Unknown type attribute %s"), type);
         goto error;
    }
    VIR_FREE(type);

    cur = obj->children;
    while(cur != NULL)
    {
        if (cur->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(cur->name, BAD_CAST "source")) {
                 char * name =  virXMLPropString(cur, "name");

                 if (name != NULL) {
                     strncpy(fs->tmpl, name,sizeof(fs->tmpl));
                     fs->tmpl[sizeof(fs->tmpl) - 1] = '\0';
                 }
                 VIR_FREE(name);
            } else if (xmlStrEqual(cur->name, BAD_CAST "quota")) {
                 char * qtype =  virXMLPropString(cur, "type");
                 char * max =  virXMLPropString(cur, "max");

                 if (qtype != NULL && STREQ(qtype, "size") && max != NULL)
                      fs->disksize = strtoI(max);
                 else if (qtype != NULL && STREQ(qtype, "inodes") && max != NULL)
                      fs->diskinodes = strtoI(max);
                 VIR_FREE(qtype);
                 VIR_FREE(max);
            }
        }
        cur = cur->next;
    }
    VIR_FREE(nodes);

    return 0;

 error:
    VIR_FREE(nodes);
    VIR_FREE(type);

    return -1;
}


379 380 381 382
/*
 * Parses a libvirt XML definition of a guest, and populates the
 * the openvz_vm struct with matching data about the guests config
 */
383
static struct openvz_vm_def
384 385
*openvzParseXML(virConnectPtr conn,
                        xmlDocPtr xml) {
386
    xmlNodePtr root = NULL;
D
Daniel Veillard 已提交
387
    char *prop = NULL;
388 389
    xmlXPathContextPtr ctxt = NULL;
    xmlXPathObjectPtr obj = NULL;
D
Daniel Veillard 已提交
390 391 392
    struct openvz_vm_def *def = NULL;
    xmlNodePtr *nodes = NULL;
    int i, n;
393

394
    if (VIR_ALLOC(def) < 0) {
D
Daniel Veillard 已提交
395
        openvzError(conn, VIR_ERR_NO_MEMORY, _("xmlXPathContext"));
396 397 398 399 400 401
        return NULL;
    }

    /* Prepare parser / xpath context */
    root = xmlDocGetRootElement(xml);
    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
D
Daniel Veillard 已提交
402
        openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("incorrect root element"));
403 404 405 406 407
        goto bail_out;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
D
Daniel Veillard 已提交
408
        openvzError(conn, VIR_ERR_NO_MEMORY, _("xmlXPathContext"));
409 410
        goto bail_out;
    }
D
Daniel Veillard 已提交
411
    ctxt->node = root;
412

413
    /* Find out what type of OPENVZ virtualization to use */
D
Daniel Veillard 已提交
414
    if (!(prop = virXMLPropString(root, "type"))) {
D
Daniel Veillard 已提交
415
        openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("missing domain type attribute"));
416 417 418
        goto bail_out;
    }

D
Daniel Veillard 已提交
419
    if (STRNEQ(prop, "openvz")){
D
Daniel Veillard 已提交
420
        openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("invalid domain type attribute"));
421 422
        goto bail_out;
    }
423
    VIR_FREE(prop);
424 425 426

    /* Extract domain name */
    obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
427
    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
428
        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
D
Daniel Veillard 已提交
429
        openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("invalid domain name"));
430 431
        goto bail_out;
    }
432

433
    /* rejecting VPS ID <= OPENVZ_RSRV_VM_LIMIT for they are reserved */
434
    if (strtoI((const char *) obj->stringval) <= OPENVZ_RSRV_VM_LIMIT) {
D
Daniel Veillard 已提交
435
        openvzError(conn, VIR_ERR_INTERNAL_ERROR,
436
              _("VPS ID Error (must be an integer greater than 100"));
437 438
        goto bail_out;
    }
439
    strncpy(def->name, (const char *) obj->stringval, OPENVZ_NAME_MAX);
440
    xmlXPathFreeObject(obj);
D
Daniel Veillard 已提交
441
    obj = NULL;
442 443

    /* Extract domain uuid */
444
    prop = virXPathString(conn, "string(./uuid[1])", ctxt);
D
Daniel Veillard 已提交
445
    if (!prop) {
446 447
        int err;
        if ((err = virUUIDGenerate(def->uuid))) {
D
Daniel Veillard 已提交
448 449 450
            openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                 _("Failed to generate UUID: %s"),
                                 strerror(err));
451 452
            goto bail_out;
        }
D
Daniel Veillard 已提交
453 454 455 456 457 458 459
    } else {
        if (virUUIDParse(prop, def->uuid) < 0) {
            openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                                _("malformed uuid element"));
            goto bail_out;
        }
        VIR_FREE(prop);
460 461
    }

D
Daniel Veillard 已提交
462
    /* extract virtual CPUs */
463
    if (virXPathULong(conn, "string(./vcpu[1])", ctxt, &def->vcpus) < 0)
D
Daniel Veillard 已提交
464
        def->vcpus = 0; //use default CPUs count
465

D
Daniel Veillard 已提交
466 467
    /* Extract filesystem info */
    if (openvzParseDomainFS(conn, &(def->fs), ctxt)) {
D
Daniel Veillard 已提交
468
        openvzError(conn, VIR_ERR_INTERNAL_ERROR,
D
Daniel Veillard 已提交
469
                               _("malformed filesystem tag"));
470 471 472
        goto bail_out;
    }

D
Daniel Veillard 已提交
473
    /* analysis of the network devices */
474 475
    if ((n = virXPathNodeSet(conn, "/domain/devices/interface",
                             ctxt, &nodes)) < 0) {
D
Daniel Veillard 已提交
476
        openvzError(conn, VIR_ERR_INTERNAL_ERROR,
D
Daniel Veillard 已提交
477
                             "%s", _("cannot extract network devices"));
478 479
        goto bail_out;
    }
480

D
Daniel Veillard 已提交
481 482 483 484 485
    for (i = n - 1 ; i >= 0 ; i--) {
        virDomainNetDefPtr net = virDomainNetDefParseXML(conn,
                                                         nodes[i]);
        if (!net)
            goto bail_out;
486

D
Daniel Veillard 已提交
487 488
        net->next = def->net;
        def->net = net;
489
    }
D
Daniel Veillard 已提交
490
    VIR_FREE(nodes);
491 492

    xmlXPathFreeContext(ctxt);
493 494 495
    return def;

 bail_out:
496
    VIR_FREE(prop);
497
    xmlXPathFreeObject(obj);
498
    xmlXPathFreeContext(ctxt);
499 500
    openvzFreeVMDef(def);

501
    return NULL;
502 503 504 505 506 507 508
}

struct openvz_vm *
openvzGetVPSInfo(virConnectPtr conn) {
    FILE *fp;
    int veid, ret;
    char status[16];
509
    char uuidstr[VIR_UUID_STRING_BUFLEN];
510 511 512 513
    struct openvz_vm *vm;
    struct openvz_vm  **pnext;
    struct openvz_driver *driver;
    struct openvz_vm_def *vmdef;
514
    char temp[124];
515 516 517 518 519

    vm =  NULL;
    driver = conn->privateData;
    driver->num_active = 0;
    driver->num_inactive = 0;
520

521
    if((fp = popen(VZLIST " -a -ovpsid,status -H 2>/dev/null", "r")) == NULL) {
D
Daniel Veillard 已提交
522
        openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("popen failed"));
523 524
        return NULL;
    }
525 526
    pnext = &vm;
    while(!feof(fp)) {
527
        if (VIR_ALLOC(*pnext) < 0) {
D
Daniel Veillard 已提交
528
            openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("calloc failed"));
529
            goto error;
530
        }
531

532 533 534
        if(!vm)
            vm = *pnext;

535
        if (fscanf(fp, "%d %s\n", &veid, status) != 2) {
D
Daniel Veillard 已提交
536
            openvzError(conn, VIR_ERR_INTERNAL_ERROR,
537 538 539
                  _("Failed to parse vzlist output"));
            goto error;
        }
540
        if(STRNEQ(status, "stopped")) {
541 542 543 544 545 546 547
            (*pnext)->status = VIR_DOMAIN_RUNNING;
            driver->num_active ++;
            (*pnext)->vpsid = veid;
        }
        else {
            (*pnext)->status = VIR_DOMAIN_SHUTOFF;
            driver->num_inactive ++;
548 549 550 551
            /*
             * inactive domains don't have their ID set in libvirt,
             * thought this doesn't make sense for OpenVZ
             */
552
            (*pnext)->vpsid = -1;
553 554
        }

555
        if (VIR_ALLOC(vmdef) < 0) {
D
Daniel Veillard 已提交
556
            openvzError(conn, VIR_ERR_INTERNAL_ERROR, _("calloc failed"));
557
            goto error;
558
        }
559

560 561
        snprintf(vmdef->name, OPENVZ_NAME_MAX,  "%i", veid);
        openvzGetVPSUUID(veid, uuidstr);
562 563 564
        ret = virUUIDParse(uuidstr, vmdef->uuid);

        if(ret == -1) {
D
Daniel Veillard 已提交
565
            openvzError(conn, VIR_ERR_INTERNAL_ERROR,
566
                  _("UUID in config file malformed"));
567
            VIR_FREE(vmdef);
568
            goto error;
569 570
        }

571 572 573 574 575 576 577 578 579 580 581
        /*get VCPU*/
        ret = openvzReadConfigParam(veid, "CPUS", temp, sizeof(temp));
        if (ret < 0) {
             openvzError(conn, VIR_ERR_INTERNAL_ERROR,
                            _("Cound not read config for container %d"), veid);
             goto error;
        } else if (ret > 0) {
             vmdef->vcpus = strtoI(temp);
        }


582 583 584 585
        (*pnext)->vmdef = vmdef;
        pnext = &(*pnext)->next;
    }
    return vm;
586 587 588 589
error:
    while (vm != NULL) {
        struct openvz_vm *next;

590
        next = vm->next;
591 592
        VIR_FREE(vm->vmdef);
        VIR_FREE(vm);
593
        vm = next;
594 595
    }
    return NULL;
596 597
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
/*
* Read parameter from container config
* sample: 133, "OSTEMPLATE", value, 1024
* return: -1 - error
*	   0 - don't found
*          1 - OK
*/
int
openvzReadConfigParam(int vpsid ,const char * param, char *value, int maxlen)
{
    char conf_file[PATH_MAX] ;
    char line[PATH_MAX] ;
    int ret, found = 0;
    int fd ;
    char * sf, * token;
    char *saveptr = NULL;

615
    if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
616 617 618 619 620
        return -1;

    value[0] = 0;

    fd = open(conf_file, O_RDONLY);
D
Daniel Veillard 已提交
621
    if (fd == -1)
622 623 624 625 626 627 628
        return -1;

    while(1) {
        ret = openvz_readline(fd, line, sizeof(line));
        if(ret <= 0)
            break;
        saveptr = NULL;
D
Daniel Veillard 已提交
629
        if (STREQLEN(line, param, strlen(param))) {
630 631 632 633 634 635 636
            sf = line;
            sf += strlen(param);
            if (sf[0] == '=' && (token = strtok_r(sf,"\"\t=\n", &saveptr)) != NULL) {
                strncpy(value, token, maxlen) ;
                value[maxlen-1] = '\0';
                found = 1;
            }
D
Daniel Veillard 已提交
637
       }
638 639 640 641 642 643 644 645 646
    }
    close(fd);

    if (ret == 0 && found)
        ret = 1;

    return ret ;
}

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
/* Locate config file of container
* return -1 - error
*         0 - OK
*/
static int
openvzLocateConfFile(int vpsid, char *conffile, int maxlen)
{
    char * confdir;
    int ret = 0;

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

    if (snprintf(conffile, maxlen, "%s/%d.conf", confdir, vpsid) >= maxlen)
        ret = -1;

    VIR_FREE(confdir);
    return ret;
}

668
static char
669
*openvzLocateConfDir(void)
670 671 672 673 674 675
{
    const char *conf_dir_list[] = {"/etc/vz/conf", "/usr/local/etc/conf", NULL};
    int i=0;

    while(conf_dir_list[i]) {
        if(!access(conf_dir_list[i], F_OK))
676
            return strdup(conf_dir_list[i]);
677 678 679 680 681 682 683
        i ++;
    }

    return NULL;
}

/* Richard Steven's classic readline() function */
684
int
685
openvz_readline(int fd, char *ptr, int maxlen)
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
{
    int n, rc;
    char c;

    for(n = 1; n < maxlen; n ++) {
        if( (rc = read(fd, &c, 1)) == 1) {
            *ptr++ = c;
            if(c == '\n')
                break;
        }
        else if(rc == 0) {
            if(n == 1)
                return 0; /* EOF condition */
            else
                break;
        }
        else
            return -1; /* error */
    }
    *ptr = 0;
    return n;
}

709
static int
710
openvzGetVPSUUID(int vpsid, char *uuidstr)
711 712 713
{
    char conf_file[PATH_MAX];
    char line[1024];
714
    char uuidbuf[1024];
715 716 717
    char iden[1024];
    int fd, ret;

718 719
   if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
       return -1;
720

721
    fd = open(conf_file, O_RDONLY);
722 723 724 725 726 727 728 729 730
    if(fd == -1)
        return -1;

    while(1) {
        ret = openvz_readline(fd, line, sizeof(line));
        if(ret == -1)
            return -1;

        if(ret == 0) { /* EoF, UUID was not found */
731
            uuidstr[0] = 0;
732 733 734
            break;
        }

735
        sscanf(line, "%s %s\n", iden, uuidbuf);
736
        if(STREQ(iden, "#UUID:")) {
737
            strncpy(uuidstr, uuidbuf, VIR_UUID_STRING_BUFLEN);
738 739 740
            break;
        }
    }
741 742
    close(fd);

743 744 745 746 747 748
    return 0;
}

/* Do actual checking for UUID presence in conf file,
 * assign if not present.
 */
749 750
int
openvzSetDefinedUUID(int vpsid, unsigned char *uuid)
751 752
{
    char conf_file[PATH_MAX];
753
    char uuidstr[VIR_UUID_STRING_BUFLEN];
754 755 756

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

758 759
   if (openvzLocateConfFile(vpsid, conf_file, PATH_MAX)<0)
       return -1;
760

J
Jim Meyering 已提交
761
    if (openvzGetVPSUUID(vpsid, uuidstr))
762 763
        return -1;

J
Jim Meyering 已提交
764
    if (uuidstr[0] == 0) {
765 766 767
        FILE *fp = fopen(conf_file, "a"); /* append */
        if (fp == NULL)
          return -1;
768

769 770
        virUUIDFormat(uuid, uuidstr);

771 772 773 774 775
        /* Record failure if fprintf or fclose fails,
           and be careful always to close the stream.  */
        if ((fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0)
            + (fclose(fp) == EOF))
            return -1;
776 777 778 779 780
    }

    return 0;
}

781 782 783 784 785 786 787 788 789
static int
openvzSetUUID(int vpsid){
    unsigned char uuid[VIR_UUID_BUFLEN];

    virUUIDGenerate(uuid);

    return openvzSetDefinedUUID(vpsid, uuid);
}

790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
/*
 * 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.
 *
 */

int openvzAssignUUIDs(void)
{
    DIR *dp;
    struct dirent *dent;
    char *conf_dir;
    int vpsid, res;
    char ext[8];

    conf_dir = openvzLocateConfDir();
806 807
    if (conf_dir == NULL)
        return -1;
808 809 810

    dp = opendir(conf_dir);
    if(dp == NULL) {
811
        VIR_FREE(conf_dir);
812 813 814 815 816
        return 0;
    }

    while((dent = readdir(dp))) {
        res = sscanf(dent->d_name, "%d.%5s", &vpsid, ext);
817
        if(!(res == 2 && STREQ(ext, "conf")))
818 819 820 821 822
            continue;
        if(vpsid > 0) /* '0.conf' belongs to the host, ignore it */
            openvzSetUUID(vpsid);
    }
    closedir(dp);
823
    VIR_FREE(conf_dir);
824 825 826
    return 0;
}