lxc_driver.c 35.0 KB
Newer Older
D
Daniel Veillard 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * Copyright IBM Corp. 2008
 *
 * lxc_driver.c: linux container driver functions
 *
 * Authors:
 *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
 *
 * 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
 */

#include <config.h>

26
#include <fcntl.h>
D
Daniel Veillard 已提交
27 28
#include <sched.h>
#include <sys/utsname.h>
D
David L. Leskovec 已提交
29
#include <stdbool.h>
D
Daniel Veillard 已提交
30 31
#include <string.h>
#include <sys/types.h>
32 33 34
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
D
Daniel Veillard 已提交
35 36 37
#include <unistd.h>
#include <wait.h>

38
#include "virterror_internal.h"
39
#include "logging.h"
40
#include "datatypes.h"
D
Daniel Veillard 已提交
41
#include "lxc_conf.h"
42
#include "lxc_container.h"
D
Daniel Veillard 已提交
43
#include "lxc_driver.h"
44
#include "memory.h"
45
#include "util.h"
46 47
#include "bridge.h"
#include "veth.h"
48
#include "event.h"
D
Dan Smith 已提交
49
#include "cgroup.h"
50

D
Daniel Veillard 已提交
51

52 53
static int lxcStartup(void);
static int lxcShutdown(void);
54
static lxc_driver_t *lxc_driver = NULL;
D
Daniel Veillard 已提交
55 56 57

/* Functions */

58
static int lxcProbe(void)
D
Daniel Veillard 已提交
59
{
60 61 62
    if (getuid() != 0 ||
        lxcContainerAvailable(0) < 0)
        return 0;
63

64
    return 1;
D
Daniel Veillard 已提交
65 66 67 68 69 70
}

static virDrvOpenStatus lxcOpen(virConnectPtr conn,
                                virConnectAuthPtr auth ATTRIBUTE_UNUSED,
                                int flags ATTRIBUTE_UNUSED)
{
71
    if (!lxcProbe())
D
Daniel Veillard 已提交
72 73
        goto declineConnection;

74 75 76
    if (lxc_driver == NULL)
        goto declineConnection;

D
Daniel Veillard 已提交
77
    /* Verify uri was specified */
78 79 80 81 82 83 84 85
    if (conn->uri == NULL) {
        conn->uri = xmlParseURI("lxc:///");
        if (!conn->uri) {
            lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
            return VIR_DRV_OPEN_ERROR;
        }
    } else if (conn->uri->scheme == NULL ||
               STRNEQ(conn->uri->scheme, "lxc")) {
D
Daniel Veillard 已提交
86 87 88
        goto declineConnection;
    }

89
    conn->privateData = lxc_driver;
D
Daniel Veillard 已提交
90 91 92 93 94 95 96 97 98

    return VIR_DRV_OPEN_SUCCESS;

declineConnection:
    return VIR_DRV_OPEN_DECLINED;
}

static int lxcClose(virConnectPtr conn)
{
99 100
    conn->privateData = NULL;
    return 0;
D
Daniel Veillard 已提交
101 102 103 104 105
}

static virDomainPtr lxcDomainLookupByID(virConnectPtr conn,
                                        int id)
{
106 107 108
    lxc_driver_t *driver = conn->privateData;
    virDomainObjPtr vm;
    virDomainPtr dom = NULL;
D
Daniel Veillard 已提交
109

110
    vm = virDomainFindByID(&driver->domains, id);
D
Daniel Veillard 已提交
111 112
    if (!vm) {
        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
113
        goto cleanup;
D
Daniel Veillard 已提交
114 115 116
    }

    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
117
    if (dom)
D
Daniel Veillard 已提交
118 119
        dom->id = vm->def->id;

120
cleanup:
D
Daniel Veillard 已提交
121 122 123 124 125 126
    return dom;
}

static virDomainPtr lxcDomainLookupByUUID(virConnectPtr conn,
                                          const unsigned char *uuid)
{
127 128 129
    lxc_driver_t *driver = conn->privateData;
    virDomainObjPtr vm;
    virDomainPtr dom = NULL;
D
Daniel Veillard 已提交
130

131
    vm = virDomainFindByUUID(&driver->domains, uuid);
D
Daniel Veillard 已提交
132 133
    if (!vm) {
        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
134
        goto cleanup;
D
Daniel Veillard 已提交
135 136 137
    }

    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
138
    if (dom)
D
Daniel Veillard 已提交
139 140
        dom->id = vm->def->id;

141
cleanup:
D
Daniel Veillard 已提交
142 143 144 145 146 147
    return dom;
}

static virDomainPtr lxcDomainLookupByName(virConnectPtr conn,
                                          const char *name)
{
148 149 150
    lxc_driver_t *driver = conn->privateData;
    virDomainObjPtr vm;
    virDomainPtr dom = NULL;
D
Daniel Veillard 已提交
151

152
    vm = virDomainFindByName(&driver->domains, name);
D
Daniel Veillard 已提交
153 154
    if (!vm) {
        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
155
        goto cleanup;
D
Daniel Veillard 已提交
156 157 158
    }

    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
159
    if (dom)
D
Daniel Veillard 已提交
160 161
        dom->id = vm->def->id;

162
cleanup:
D
Daniel Veillard 已提交
163 164 165
    return dom;
}

166
static int lxcListDomains(virConnectPtr conn, int *ids, int nids) {
167
    lxc_driver_t *driver = conn->privateData;
168 169 170 171 172 173
    int got = 0, i;

    for (i = 0 ; i < driver->domains.count && got < nids ; i++)
        if (virDomainIsActive(driver->domains.objs[i]))
            ids[got++] = driver->domains.objs[i]->def->id;

174
    return got;
D
Daniel Veillard 已提交
175
}
176

177
static int lxcNumDomains(virConnectPtr conn) {
178
    lxc_driver_t *driver = conn->privateData;
179 180 181 182
    int n = 0, i;

    for (i = 0 ; i < driver->domains.count ; i++)
        if (virDomainIsActive(driver->domains.objs[i]))
183
            n++;
184

185
    return n;
D
Daniel Veillard 已提交
186 187 188
}

static int lxcListDefinedDomains(virConnectPtr conn,
189
                                 char **const names, int nnames) {
190
    lxc_driver_t *driver = conn->privateData;
191
    int got = 0, i;
192 193 194 195 196 197

    for (i = 0 ; i < driver->domains.count && got < nnames ; i++) {
        if (!virDomainIsActive(driver->domains.objs[i])) {
            if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) {
                lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
                         "%s", _("failed to allocate space for VM name string"));
D
Daniel Veillard 已提交
198 199 200 201
                goto cleanup;
            }
        }
    }
202

203
    return got;
D
Daniel Veillard 已提交
204 205

 cleanup:
206
    for (i = 0 ; i < got ; i++)
207
        VIR_FREE(names[i]);
D
Daniel Veillard 已提交
208 209 210 211
    return -1;
}


212
static int lxcNumDefinedDomains(virConnectPtr conn) {
213
    lxc_driver_t *driver = conn->privateData;
214 215 216 217
    int n = 0, i;

    for (i = 0 ; i < driver->domains.count ; i++)
        if (!virDomainIsActive(driver->domains.objs[i]))
218
            n++;
219

220
    return n;
D
Daniel Veillard 已提交
221 222
}

223 224


D
Daniel Veillard 已提交
225 226
static virDomainPtr lxcDomainDefine(virConnectPtr conn, const char *xml)
{
227 228
    lxc_driver_t *driver = conn->privateData;
    virDomainDefPtr def = NULL;
229
    virDomainObjPtr vm;
230
    virDomainPtr dom = NULL;
D
Daniel Veillard 已提交
231

232
    if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
233
        goto cleanup;
D
Daniel Veillard 已提交
234

235 236
    if ((def->nets != NULL) && !(driver->have_netns)) {
        lxcError(conn, NULL, VIR_ERR_NO_SUPPORT,
J
Jim Meyering 已提交
237
                 "%s", _("System lacks NETNS support"));
238
        goto cleanup;
239 240
    }

241 242 243
    if (!(vm = virDomainAssignDef(conn, &driver->domains, def)))
        goto cleanup;
    def = NULL;
244
    vm->persistent = 1;
D
Daniel Veillard 已提交
245

246 247
    if (virDomainSaveConfig(conn,
                            driver->configDir,
248
                            vm->newDef ? vm->newDef : vm->def) < 0) {
249
        virDomainRemoveInactive(&driver->domains, vm);
250
        goto cleanup;
D
Daniel Veillard 已提交
251 252 253
    }

    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
254
    if (dom)
D
Daniel Veillard 已提交
255 256
        dom->id = vm->def->id;

257 258
cleanup:
    virDomainDefFree(def);
D
Daniel Veillard 已提交
259 260 261 262 263
    return dom;
}

static int lxcDomainUndefine(virDomainPtr dom)
{
264 265 266
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    int ret = -1;
D
Daniel Veillard 已提交
267

268
    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
D
Daniel Veillard 已提交
269 270
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
J
Jim Meyering 已提交
271
                 "%s", _("no domain with matching uuid"));
272
        goto cleanup;
D
Daniel Veillard 已提交
273 274
    }

275
    if (virDomainIsActive(vm)) {
D
Daniel Veillard 已提交
276
        lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
277
                 "%s", _("cannot delete active domain"));
278
        goto cleanup;
D
Daniel Veillard 已提交
279 280
    }

281 282 283
    if (!vm->persistent) {
        lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
                 "%s", _("cannot undefine transient domain"));
284
        goto cleanup;
285
    }
D
Daniel Veillard 已提交
286

287 288 289
    if (virDomainDeleteConfig(dom->conn,
                              driver->configDir,
                              driver->autostartDir,
290 291
                              vm) < 0)
        goto cleanup;
D
Daniel Veillard 已提交
292

293
    virDomainRemoveInactive(&driver->domains, vm);
294
    ret = 0;
D
Daniel Veillard 已提交
295

296 297
cleanup:
    return ret;
D
Daniel Veillard 已提交
298 299 300 301 302
}

static int lxcDomainGetInfo(virDomainPtr dom,
                            virDomainInfoPtr info)
{
303 304 305
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    int ret = -1;
D
Daniel Veillard 已提交
306

307
    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
D
Daniel Veillard 已提交
308 309
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
J
Jim Meyering 已提交
310
                 "%s", _("no domain with matching uuid"));
311
        goto cleanup;
D
Daniel Veillard 已提交
312 313 314 315
    }

    info->state = vm->state;

316
    if (!virDomainIsActive(vm)) {
D
Daniel Veillard 已提交
317 318 319 320 321
        info->cpuTime = 0;
    } else {
        info->cpuTime = 0;
    }

322 323
    info->maxMem = vm->def->maxmem;
    info->memory = vm->def->memory;
D
Daniel Veillard 已提交
324
    info->nrVirtCpu = 1;
325
    ret = 0;
D
Daniel Veillard 已提交
326

327 328
cleanup:
    return ret;
D
Daniel Veillard 已提交
329 330
}

331
static char *lxcGetOSType(virDomainPtr dom)
D
Daniel Veillard 已提交
332
{
333 334 335
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    char *ret = NULL;
336

337
    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
338 339
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
J
Jim Meyering 已提交
340
                 "%s", _("no domain with matching uuid"));
341
        goto cleanup;
342 343
    }

344 345 346 347
    ret = strdup(vm->def->os.type);

cleanup:
    return ret;
D
Daniel Veillard 已提交
348 349 350
}

static char *lxcDomainDumpXML(virDomainPtr dom,
351
                              int flags)
D
Daniel Veillard 已提交
352
{
353 354 355
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    char *ret = NULL;
D
Daniel Veillard 已提交
356

357
    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
D
Daniel Veillard 已提交
358 359
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
J
Jim Meyering 已提交
360
                 "%s", _("no domain with matching uuid"));
361
        goto cleanup;
D
Daniel Veillard 已提交
362 363
    }

364 365 366 367 368 369 370
    ret = virDomainDefFormat(dom->conn,
                             (flags & VIR_DOMAIN_XML_INACTIVE) &&
                             vm->newDef ? vm->newDef : vm->def,
                             flags);

cleanup:
    return ret;
D
Daniel Veillard 已提交
371 372
}

373 374 375 376 377 378 379 380 381 382 383 384 385

/**
 * lxcVmCleanup:
 * @vm: Ptr to VM to clean up
 *
 * waitpid() on the container process.  kill and wait the tty process
 * This is called by both lxcDomainDestroy and lxcSigHandler when a
 * container exits.
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcVMCleanup(virConnectPtr conn,
                        lxc_driver_t *driver,
386
                        virDomainObjPtr  vm)
387 388 389 390
{
    int rc = -1;
    int waitRc;
    int childStatus = -1;
D
Dan Smith 已提交
391
    virCgroupPtr cgroup;
392
    int i;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

    while (((waitRc = waitpid(vm->pid, &childStatus, 0)) == -1) &&
           errno == EINTR)
        ; /* empty */

    if ((waitRc != vm->pid) && (errno != ECHILD)) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("waitpid failed to wait for container %d: %d %s"),
                 vm->pid, waitRc, strerror(errno));
    }

    rc = 0;

    if (WIFEXITED(childStatus)) {
        rc = WEXITSTATUS(childStatus);
        DEBUG("container exited with rc: %d", rc);
    }

411
    virEventRemoveHandle(vm->monitorWatch);
412 413 414
    close(vm->monitor);

    virFileDeletePid(driver->stateDir, vm->def->name);
415
    virDomainDeleteConfig(conn, driver->stateDir, NULL, vm);
416 417 418 419 420 421

    vm->state = VIR_DOMAIN_SHUTOFF;
    vm->pid = -1;
    vm->def->id = -1;
    vm->monitor = -1;

422 423 424
    for (i = 0 ; i < vm->def->nnets ; i++) {
        vethInterfaceUpOrDown(vm->def->nets[i]->ifname, 0);
        vethDelete(vm->def->nets[i]->ifname);
425 426
    }

D
Dan Smith 已提交
427 428 429 430 431
    if (virCgroupForDomain(vm->def, "lxc", &cgroup) == 0) {
        virCgroupRemove(cgroup);
        virCgroupFree(&cgroup);
    }

432 433 434
    return rc;
}

435 436
/**
 * lxcSetupInterfaces:
437
 * @def: pointer to virtual machine structure
438 439 440 441 442 443 444 445
 *
 * Sets up the container interfaces by creating the veth device pairs and
 * attaching the parent end to the appropriate bridge.  The container end
 * will moved into the container namespace later after clone has been called.
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcSetupInterfaces(virConnectPtr conn,
446
                              virDomainDefPtr def,
447 448
                              unsigned int *nveths,
                              char ***veths)
449
{
450
    int rc = -1, i;
451
    char *bridge = NULL;
452 453
    char parentVeth[PATH_MAX] = "";
    char containerVeth[PATH_MAX] = "";
454
    brControl *brctl = NULL;
455

456
    if (brInit(&brctl) != 0)
457 458
        return -1;

459 460
    for (i = 0 ; i < def->nnets ; i++) {
        switch (def->nets[i]->type) {
461 462 463
        case VIR_DOMAIN_NET_TYPE_NETWORK:
        {
            virNetworkPtr network = virNetworkLookupByName(conn,
464
                                                           def->nets[i]->data.network.name);
465 466 467 468 469 470 471
            if (!network) {
                goto error_exit;
            }

            bridge = virNetworkGetBridgeName(network);

            virNetworkFree(network);
472 473 474
            break;
        }
        case VIR_DOMAIN_NET_TYPE_BRIDGE:
475
            bridge = def->nets[i]->data.bridge.brname;
476
            break;
477 478 479 480 481
        }

        DEBUG("bridge: %s", bridge);
        if (NULL == bridge) {
            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
482
                     "%s", _("failed to get bridge for interface"));
483 484 485 486
            goto error_exit;
        }

        DEBUG0("calling vethCreate()");
487 488
        if (NULL != def->nets[i]->ifname) {
            strcpy(parentVeth, def->nets[i]->ifname);
489 490 491 492 493 494 495
        }
        DEBUG("parentVeth: %s, containerVeth: %s", parentVeth, containerVeth);
        if (0 != (rc = vethCreate(parentVeth, PATH_MAX, containerVeth, PATH_MAX))) {
            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                     _("failed to create veth device pair: %d"), rc);
            goto error_exit;
        }
496 497
        if (NULL == def->nets[i]->ifname) {
            def->nets[i]->ifname = strdup(parentVeth);
498
        }
499 500 501
        if (VIR_REALLOC_N(*veths, (*nveths)+1) < 0)
            goto error_exit;
        if (((*veths)[(*nveths)++] = strdup(containerVeth)) == NULL)
502 503
            goto error_exit;

504
        if (NULL == def->nets[i]->ifname) {
505
            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
506
                     "%s", _("failed to allocate veth names"));
507 508 509
            goto error_exit;
        }

510
        if (0 != (rc = brAddInterface(brctl, bridge, parentVeth))) {
511 512 513 514 515 516 517 518 519
            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                     _("failed to add %s device to %s: %s"),
                     parentVeth,
                     bridge,
                     strerror(rc));
            goto error_exit;
        }

        if (0 != (rc = vethInterfaceUpOrDown(parentVeth, 1))) {
520
            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
521 522 523 524 525 526 527 528 529
                     _("failed to enable parent ns veth device: %d"), rc);
            goto error_exit;
        }

    }

    rc = 0;

error_exit:
530
    brShutdown(brctl);
531 532 533
    return rc;
}

534

535 536
static int lxcMonitorClient(virConnectPtr conn,
                            lxc_driver_t * driver,
537
                            virDomainObjPtr vm)
538
{
539 540 541
    char *sockpath = NULL;
    int fd;
    struct sockaddr_un addr;
542

543 544 545 546 547 548 549
    if (asprintf(&sockpath, "%s/%s.sock",
                 driver->stateDir, vm->def->name) < 0) {
        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
        return -1;
    }

    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
550
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
551 552 553
                 _("failed to create client socket: %s"),
                 strerror(errno));
        goto error;
554 555
    }

556 557 558 559 560
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path));

    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
561
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
562 563 564
                 _("failed to connect to client socket: %s"),
                 strerror(errno));
        goto error;
565 566
    }

567 568
    VIR_FREE(sockpath);
    return fd;
569

570 571 572 573 574 575 576 577 578 579
error:
    VIR_FREE(sockpath);
    if (fd != -1)
        close(fd);
    return -1;
}


static int lxcVmTerminate(virConnectPtr conn,
                          lxc_driver_t *driver,
580
                          virDomainObjPtr vm,
581 582 583 584
                          int signum)
{
    if (signum == 0)
        signum = SIGINT;
585

586 587 588 589 590 591
    if (vm->pid <= 0) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("invalid PID %d for container"), vm->pid);
        return -1;
    }

592 593
    if (kill(vm->pid, signum) < 0) {
        if (errno != ESRCH) {
594
            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
595 596 597
                     _("failed to kill pid %d: %s"),
                     vm->pid, strerror(errno));
            return -1;
598
        }
599 600
    }

601
    vm->state = VIR_DOMAIN_SHUTDOWN;
602

603 604
    return lxcVMCleanup(conn, driver, vm);
}
605

606 607
static void lxcMonitorEvent(int watch,
                            int fd,
608 609 610 611
                            int events ATTRIBUTE_UNUSED,
                            void *data)
{
    lxc_driver_t *driver = data;
612 613
    virDomainObjPtr vm = NULL;
    unsigned int i;
614

615
    for (i = 0 ; i < driver->domains.count ; i++) {
616
        if (driver->domains.objs[i]->monitorWatch == watch) {
617
            vm = driver->domains.objs[i];
618
            break;
619
        }
620 621
    }
    if (!vm) {
622 623 624 625 626 627
        virEventRemoveHandle(watch);
        return;
    }

    if (vm->monitor != fd) {
        virEventRemoveHandle(watch);
628
        return;
629 630
    }

631
    if (lxcVmTerminate(NULL, driver, vm, SIGINT) < 0)
632
        virEventRemoveHandle(watch);
633 634 635
}


636 637 638 639 640 641 642 643 644 645 646 647 648 649
static int lxcControllerStart(virConnectPtr conn,
                              virDomainObjPtr vm,
                              int nveths,
                              char **veths,
                              int appPty,
                              int logfd)
{
    int i;
    int rc;
    int ret = -1;
    int largc = 0, larga = 0;
    const char **largv = NULL;
    pid_t child;
    int status;
650 651
    fd_set keepfd;
    char appPtyStr[30];
652 653
    const char *emulator;
    lxc_driver_t *driver = conn->privateData;
654 655

    FD_ZERO(&keepfd);
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

#define ADD_ARG_SPACE                                                   \
    do { \
        if (largc == larga) {                                           \
            larga += 10;                                                \
            if (VIR_REALLOC_N(largv, larga) < 0)                        \
                goto no_memory;                                         \
        }                                                               \
    } while (0)

#define ADD_ARG(thisarg)                                                \
    do {                                                                \
        ADD_ARG_SPACE;                                                  \
        largv[largc++] = thisarg;                                       \
    } while (0)

#define ADD_ARG_LIT(thisarg)                                            \
    do {                                                                \
        ADD_ARG_SPACE;                                                  \
        if ((largv[largc++] = strdup(thisarg)) == NULL)                 \
            goto no_memory;                                             \
    } while (0)

679 680
    snprintf(appPtyStr, sizeof(appPtyStr), "%d", appPty);

681 682 683 684 685 686 687
    emulator = vm->def->emulator;
    if (!emulator)
        emulator = virDomainDefDefaultEmulator(conn, vm->def, driver->caps);
    if (!emulator)
        return -1;

    ADD_ARG_LIT(emulator);
688 689 690
    ADD_ARG_LIT("--name");
    ADD_ARG_LIT(vm->def->name);
    ADD_ARG_LIT("--console");
691
    ADD_ARG_LIT(appPtyStr);
692 693 694 695 696 697 698 699 700
    ADD_ARG_LIT("--background");

    for (i = 0 ; i < nveths ; i++) {
        ADD_ARG_LIT("--veth");
        ADD_ARG_LIT(veths[i]);
    }

    ADD_ARG(NULL);

701
    vm->stdin_fd = -1;
702 703
    vm->stdout_fd = vm->stderr_fd = logfd;

704 705 706
    FD_SET(appPty, &keepfd);

    if (virExec(conn, largv, NULL, &keepfd, &child,
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
                vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd,
                VIR_EXEC_NONE) < 0)
        goto cleanup;

    /* We now wait for the process to exit - the controller
     * will fork() itself into the background - waiting for
     * it to exit thus guarentees it has written its pidfile
     */
    while ((rc = waitpid(child, &status, 0) == -1) && errno == EINTR);
    if (rc == -1) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("cannot wait for '%s': %s"),
                 largv[0], strerror(errno));
        goto cleanup;
    }

    if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("container '%s' unexpectedly shutdown during startup"),
                 largv[0]);
        goto cleanup;
    }

#undef ADD_ARG
#undef ADD_ARG_LIT
#undef ADD_ARG_SPACE

    ret = 0;

cleanup:
    for (i = 0 ; i < largc ; i++)
        VIR_FREE(largv[i]);

    return ret;

no_memory:
    lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
    goto cleanup;
}


748 749 750 751 752 753 754 755 756 757 758 759
/**
 * lxcVmStart:
 * @conn: pointer to connection
 * @driver: pointer to driver structure
 * @vm: pointer to virtual machine structure
 *
 * Starts a vm
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcVmStart(virConnectPtr conn,
                      lxc_driver_t * driver,
760
                      virDomainObjPtr  vm)
761 762
{
    int rc = -1;
763 764
    unsigned int i;
    int parentTty;
765
    char *parentTtyPath = NULL;
766 767 768 769 770 771 772 773 774 775 776
    char *logfile = NULL;
    int logfd = -1;
    unsigned int nveths = 0;
    char **veths = NULL;

    if (virFileMakePath(driver->logDir) < 0) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("cannot create log directory %s: %s"),
                 driver->logDir, strerror(rc));
        return -1;
    }
777

778 779 780 781
    if (asprintf(&logfile, "%s/%s.log",
                 driver->logDir, vm->def->name) < 0) {
        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
        return -1;
782 783
    }

784
    /* open parent tty */
785
    if (virFileOpenTty(&parentTty, &parentTtyPath, 1) < 0) {
786
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
787
                 _("failed to allocate tty: %s"),
788 789 790
                 strerror(errno));
        goto cleanup;
    }
791 792 793 794 795 796 797
    if (vm->def->console &&
        vm->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY) {
        VIR_FREE(vm->def->console->data.file.path);
        vm->def->console->data.file.path = parentTtyPath;
    } else {
        VIR_FREE(parentTtyPath);
    }
798

799
    if (lxcSetupInterfaces(conn, vm->def, &nveths, &veths) != 0)
800
        goto cleanup;
801

802 803 804 805 806 807
    /* Persist the live configuration now we have veth & tty info */
    if (virDomainSaveConfig(conn, driver->stateDir, vm->def) < 0) {
        rc = -1;
        goto cleanup;
    }

808 809
    if ((logfd = open(logfile, O_WRONLY | O_TRUNC | O_CREAT,
             S_IRUSR|S_IWUSR)) < 0) {
810
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
811 812
                 _("failed to open %s: %s"), logfile,
                 strerror(errno));
813
        goto cleanup;
814 815
    }

816 817 818 819
    if (lxcControllerStart(conn,
                           vm,
                           nveths, veths,
                           parentTty, logfd) < 0)
820
        goto cleanup;
821 822 823 824 825

    /* Connect to the controller as a client *first* because
     * this will block until the child has written their
     * pid file out to disk */
    if ((vm->monitor = lxcMonitorClient(conn, driver, vm)) < 0)
826 827
        goto cleanup;

828 829 830 831 832 833
    /* And get its pid */
    if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) != 0) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("Failed to read pid file %s/%s.pid: %s"),
                 driver->stateDir, vm->def->name, strerror(rc));
        rc = -1;
834
        goto cleanup;
835
    }
836

837
    vm->def->id = vm->pid;
838 839
    vm->state = VIR_DOMAIN_RUNNING;

840 841 842 843
    if ((vm->monitorWatch = virEventAddHandle(
             vm->monitor,
             VIR_EVENT_HANDLE_ERROR | VIR_EVENT_HANDLE_HANGUP,
             lxcMonitorEvent,
844
             driver, NULL)) < 0) {
845 846 847
        lxcVmTerminate(conn, driver, vm, 0);
        goto cleanup;
    }
848

849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
    rc = 0;

cleanup:
    for (i = 0 ; i < nveths ; i++) {
        if (rc != 0)
            vethDelete(veths[i]);
        VIR_FREE(veths[i]);
    }
    if (rc != 0 && vm->monitor != -1) {
        close(vm->monitor);
        vm->monitor = -1;
    }
    if (parentTty != -1)
        close(parentTty);
    if (logfd != -1)
        close(logfd);
    VIR_FREE(logfile);
866 867 868 869 870 871 872 873 874 875 876 877 878
    return rc;
}

/**
 * lxcDomainStart:
 * @dom: domain to start
 *
 * Looks up domain and starts it.
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcDomainStart(virDomainPtr dom)
{
879 880 881
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    int ret = -1;
882

883
    vm = virDomainFindByName(&driver->domains, dom->name);
884
    if (!vm) {
885
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
886
                 _("no domain named %s"), dom->name);
887 888 889
        goto cleanup;
    }

890
    if ((vm->def->nets != NULL) && !(driver->have_netns)) {
891
        lxcError(dom->conn, NULL, VIR_ERR_NO_SUPPORT,
J
Jim Meyering 已提交
892
                 "%s", _("System lacks NETNS support"));
893 894 895
        goto cleanup;
    }

896
    ret = lxcVmStart(dom->conn, driver, vm);
897 898

cleanup:
899
    return ret;
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
}

/**
 * lxcDomainCreateAndStart:
 * @conn: pointer to connection
 * @xml: XML definition of domain
 * @flags: Unused
 *
 * Creates a domain based on xml and starts it
 *
 * Returns 0 on success or -1 in case of error
 */
static virDomainPtr
lxcDomainCreateAndStart(virConnectPtr conn,
                        const char *xml,
                        unsigned int flags ATTRIBUTE_UNUSED) {
916
    lxc_driver_t *driver = conn->privateData;
917 918
    virDomainObjPtr vm;
    virDomainDefPtr def;
919 920
    virDomainPtr dom = NULL;

921
    if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
922
        goto cleanup;
923

924 925
    if ((def->nets != NULL) && !(driver->have_netns)) {
        lxcError(conn, NULL, VIR_ERR_NO_SUPPORT,
J
Jim Meyering 已提交
926
                 "%s", _("System lacks NETNS support"));
927
        goto cleanup;
928 929
    }

930

931 932 933
    if (!(vm = virDomainAssignDef(conn, &driver->domains, def)))
        goto cleanup;
    def = NULL;
934 935

    if (lxcVmStart(conn, driver, vm) < 0) {
936
        virDomainRemoveInactive(&driver->domains, vm);
937
        goto cleanup;
938 939 940
    }

    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
941
    if (dom)
942 943
        dom->id = vm->def->id;

944 945
cleanup:
    virDomainDefFree(def);
946 947 948 949 950 951 952 953 954 955 956 957 958
    return dom;
}

/**
 * lxcDomainShutdown:
 * @dom: Ptr to domain to shutdown
 *
 * Sends SIGINT to container root process to request it to shutdown
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcDomainShutdown(virDomainPtr dom)
{
959 960 961
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    int ret = -1;
962

963
    vm = virDomainFindByID(&driver->domains, dom->id);
964 965 966
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
                 _("no domain with id %d"), dom->id);
967
        goto cleanup;
968 969
    }

970 971 972 973
    ret = lxcVmTerminate(dom->conn, driver, vm, 0);

cleanup:
    return ret;
974 975
}

976 977 978 979 980 981 982 983 984 985 986

/**
 * lxcDomainDestroy:
 * @dom: Ptr to domain to destroy
 *
 * Sends SIGKILL to container root process to terminate the container
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcDomainDestroy(virDomainPtr dom)
{
987 988 989
    lxc_driver_t *driver = dom->conn->privateData;
    virDomainObjPtr vm;
    int ret = -1;
990

991
    vm = virDomainFindByID(&driver->domains, dom->id);
992 993 994
    if (!vm) {
        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
                 _("no domain with id %d"), dom->id);
995
        goto cleanup;
996 997
    }

998 999 1000 1001
    ret = lxcVmTerminate(dom->conn, driver, vm, SIGKILL);

cleanup:
    return ret;
1002
}
1003

1004 1005 1006 1007 1008
static int lxcCheckNetNsSupport(void)
{
    const char *argv[] = {"ip", "link", "set", "lo", "netns", "-1", NULL};
    int ip_rc;

1009 1010 1011
    if (virRun(NULL, argv, &ip_rc) < 0 ||
        !(WIFEXITED(ip_rc) && (WEXITSTATUS(ip_rc) != 255)))
        return 0;
1012

1013 1014
    if (lxcContainerAvailable(LXC_CONTAINER_FEATURE_NET) < 0)
        return 0;
1015

1016
    return 1;
1017 1018
}

1019
static int lxcStartup(void)
D
Daniel Veillard 已提交
1020
{
1021
    uid_t uid = getuid();
1022
    unsigned int i;
1023 1024 1025 1026 1027 1028

    /* Check that the user is root */
    if (0 != uid) {
        return -1;
    }

1029
    if (VIR_ALLOC(lxc_driver) < 0) {
1030 1031
        return -1;
    }
D
Daniel Veillard 已提交
1032

1033
    /* Check that this is a container enabled kernel */
1034
    if(lxcContainerAvailable(0) < 0)
D
Daniel Veillard 已提交
1035 1036
        return -1;

1037
    lxc_driver->have_netns = lxcCheckNetNsSupport();
D
Daniel Veillard 已提交
1038 1039

    /* Call function to load lxc driver configuration information */
1040 1041
    if (lxcLoadDriverConfig(lxc_driver) < 0) {
        lxcShutdown();
D
Daniel Veillard 已提交
1042 1043 1044
        return -1;
    }

1045
    if ((lxc_driver->caps = lxcCapsInit()) == NULL) {
1046
        lxcShutdown();
D
Daniel Veillard 已提交
1047 1048 1049
        return -1;
    }

1050 1051 1052 1053
    if (virDomainLoadAllConfigs(NULL,
                                lxc_driver->caps,
                                &lxc_driver->domains,
                                lxc_driver->configDir,
1054 1055
                                lxc_driver->autostartDir,
                                NULL, NULL) < 0) {
1056 1057 1058 1059
        lxcShutdown();
        return -1;
    }

1060 1061
    for (i = 0 ; i < lxc_driver->domains.count ; i++) {
        virDomainObjPtr vm = lxc_driver->domains.objs[i];
1062 1063
        char *config = NULL;
        virDomainDefPtr tmp;
1064
        int rc;
1065
        if ((vm->monitor = lxcMonitorClient(NULL, lxc_driver, vm)) < 0)
1066 1067 1068 1069 1070 1071 1072 1073 1074
            continue;

        /* Read pid from controller */
        if ((rc = virFileReadPid(lxc_driver->stateDir, vm->def->name, &vm->pid)) != 0) {
            close(vm->monitor);
            vm->monitor = -1;
            continue;
        }

1075 1076 1077 1078 1079 1080
        if ((config = virDomainConfigFile(NULL,
                                          lxc_driver->stateDir,
                                          vm->def->name)) == NULL)
            continue;

        /* Try and load the live config */
1081
        tmp = virDomainDefParseFile(NULL, lxc_driver->caps, config, 0);
1082 1083 1084 1085 1086 1087
        VIR_FREE(config);
        if (tmp) {
            vm->newDef = vm->def;
            vm->def = tmp;
        }

1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
        if (vm->pid != 0) {
            vm->def->id = vm->pid;
            vm->state = VIR_DOMAIN_RUNNING;
        } else {
            vm->def->id = -1;
            close(vm->monitor);
            vm->monitor = -1;
        }
    }

D
Daniel Veillard 已提交
1098 1099 1100 1101 1102
    return 0;
}

static void lxcFreeDriver(lxc_driver_t *driver)
{
1103
    virCapabilitiesFree(driver->caps);
1104
    VIR_FREE(driver->configDir);
1105
    VIR_FREE(driver->autostartDir);
1106
    VIR_FREE(driver->stateDir);
1107
    VIR_FREE(driver->logDir);
1108
    VIR_FREE(driver);
D
Daniel Veillard 已提交
1109 1110
}

1111
static int lxcShutdown(void)
D
Daniel Veillard 已提交
1112
{
1113
    if (lxc_driver == NULL)
1114
        return(-1);
1115 1116

    virDomainObjListFree(&lxc_driver->domains);
1117
    lxcFreeDriver(lxc_driver);
1118
    lxc_driver = NULL;
1119 1120 1121

    return 0;
}
D
Daniel Veillard 已提交
1122

1123 1124 1125 1126 1127 1128 1129 1130 1131
/**
 * lxcActive:
 *
 * Checks if the LXC daemon is active, i.e. has an active domain
 *
 * Returns 1 if active, 0 otherwise
 */
static int
lxcActive(void) {
1132
    unsigned int i;
1133

1134 1135
    if (lxc_driver == NULL)
        return(0);
1136

1137 1138
    for (i = 0 ; i < lxc_driver->domains.count ; i++)
        if (virDomainIsActive(lxc_driver->domains.objs[i]))
1139
            return 1;
1140 1141

    /* Otherwise we're happy to deal with a shutdown */
D
Daniel Veillard 已提交
1142 1143 1144
    return 0;
}

D
Dan Smith 已提交
1145 1146 1147 1148 1149 1150 1151 1152 1153
static int lxcVersion(virConnectPtr conn, unsigned long *version)
{
    struct utsname ver;
    int maj;
    int min;
    int rev;

    if (uname(&ver) != 0) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
1154
                 _("uname(): %s"), strerror(errno));
D
Dan Smith 已提交
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
        return -1;
    }

    if (sscanf(ver.release, "%i.%i.%i", &maj, &min, &rev) != 3) {
        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("Unknown release: %s"), ver.release);
        return -1;
    }

    *version = (maj * 1000 * 1000) + (min * 1000) + rev;

    return 0;
}
1168

1169 1170
static char *lxcGetSchedulerType(virDomainPtr domain ATTRIBUTE_UNUSED,
                                 int *nparams)
1171 1172 1173 1174 1175 1176 1177
{
    if (nparams)
        *nparams = 1;

    return strdup("posix");
}

1178
static int lxcSetSchedulerParameters(virDomainPtr domain,
1179 1180 1181 1182
                                     virSchedParameterPtr params,
                                     int nparams)
{
    int i;
1183 1184 1185
    virCgroupPtr group = NULL;
    virDomainObjPtr vm = NULL;
    int ret = -1;
1186 1187

    if (virCgroupHaveSupport() != 0)
1188
        goto cleanup;
1189

1190 1191 1192 1193 1194
    vm = virDomainFindByUUID(&lxc_driver->domains, domain->uuid);
    if (vm == NULL) {
        lxcError(NULL, domain, VIR_ERR_INTERNAL_ERROR,
                 _("No such domain %s"), domain->uuid);
        goto cleanup;
1195 1196
    }

1197 1198
    if (virCgroupForDomain(vm->def, "lxc", &group) != 0)
        goto cleanup;
1199 1200 1201 1202 1203

    for (i = 0; i < nparams; i++) {
        virSchedParameterPtr param = &params[i];

        if (STREQ(param->field, "cpu_shares")) {
1204 1205
            if (virCgroupSetCpuShares(group, params[i].value.ui) != 0)
                goto cleanup;
1206
        } else {
1207
            lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
1208
                     _("Invalid parameter `%s'"), param->field);
1209
            goto cleanup;
1210 1211
        }
    }
1212
    ret = 0;
1213

1214
cleanup:
1215 1216
    virCgroupFree(&group);

1217
    return ret;
1218 1219
}

1220
static int lxcGetSchedulerParameters(virDomainPtr domain,
1221 1222 1223
                                     virSchedParameterPtr params,
                                     int *nparams)
{
1224 1225
    virCgroupPtr group = NULL;
    virDomainObjPtr vm = NULL;
1226
    unsigned long val;
1227
    int ret = -1;
1228 1229

    if (virCgroupHaveSupport() != 0)
1230
        goto cleanup;
1231 1232

    if ((*nparams) != 1) {
1233
        lxcError(NULL, domain, VIR_ERR_INVALID_ARG,
J
Jim Meyering 已提交
1234
                 "%s", _("Invalid parameter count"));
1235
        goto cleanup;
1236 1237
    }

1238 1239 1240 1241 1242
    vm = virDomainFindByUUID(&lxc_driver->domains, domain->uuid);
    if (vm == NULL) {
        lxcError(NULL, domain, VIR_ERR_INTERNAL_ERROR,
                 _("No such domain %s"), domain->uuid);
        goto cleanup;
1243 1244
    }

1245 1246
    if (virCgroupForDomain(vm->def, "lxc", &group) != 0)
        goto cleanup;
1247

1248 1249
    if (virCgroupGetCpuShares(group, &val) != 0)
        goto cleanup;
1250
    params[0].value.ul = val;
1251 1252 1253
    strncpy(params[0].field, "cpu_shares", sizeof(params[0].field));
    params[0].type = VIR_DOMAIN_SCHED_FIELD_ULLONG;

1254
    ret = 0;
1255

1256 1257 1258
cleanup:
    virCgroupFree(&group);
    return ret;
1259 1260
}

D
Daniel Veillard 已提交
1261 1262 1263 1264 1265 1266 1267 1268
/* Function Tables */
static virDriver lxcDriver = {
    VIR_DRV_LXC, /* the number virDrvNo */
    "LXC", /* the name of the driver */
    lxcOpen, /* open */
    lxcClose, /* close */
    NULL, /* supports_feature */
    NULL, /* type */
D
Dan Smith 已提交
1269
    lxcVersion, /* version */
D
Daniel Veillard 已提交
1270 1271 1272 1273 1274 1275 1276
    NULL, /* getHostname */
    NULL, /* getURI */
    NULL, /* getMaxVcpus */
    NULL, /* nodeGetInfo */
    NULL, /* getCapabilities */
    lxcListDomains, /* listDomains */
    lxcNumDomains, /* numOfDomains */
1277
    lxcDomainCreateAndStart, /* domainCreateXML */
D
Daniel Veillard 已提交
1278 1279 1280 1281 1282
    lxcDomainLookupByID, /* domainLookupByID */
    lxcDomainLookupByUUID, /* domainLookupByUUID */
    lxcDomainLookupByName, /* domainLookupByName */
    NULL, /* domainSuspend */
    NULL, /* domainResume */
1283
    lxcDomainShutdown, /* domainShutdown */
D
Daniel Veillard 已提交
1284
    NULL, /* domainReboot */
1285
    lxcDomainDestroy, /* domainDestroy */
D
Daniel Veillard 已提交
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
    lxcGetOSType, /* domainGetOSType */
    NULL, /* domainGetMaxMemory */
    NULL, /* domainSetMaxMemory */
    NULL, /* domainSetMemory */
    lxcDomainGetInfo, /* domainGetInfo */
    NULL, /* domainSave */
    NULL, /* domainRestore */
    NULL, /* domainCoreDump */
    NULL, /* domainSetVcpus */
    NULL, /* domainPinVcpu */
    NULL, /* domainGetVcpus */
    NULL, /* domainGetMaxVcpus */
    lxcDomainDumpXML, /* domainDumpXML */
    lxcListDefinedDomains, /* listDefinedDomains */
    lxcNumDefinedDomains, /* numOfDefinedDomains */
1301
    lxcDomainStart, /* domainCreate */
D
Daniel Veillard 已提交
1302 1303 1304 1305 1306 1307
    lxcDomainDefine, /* domainDefineXML */
    lxcDomainUndefine, /* domainUndefine */
    NULL, /* domainAttachDevice */
    NULL, /* domainDetachDevice */
    NULL, /* domainGetAutostart */
    NULL, /* domainSetAutostart */
1308 1309 1310
    lxcGetSchedulerType, /* domainGetSchedulerType */
    lxcGetSchedulerParameters, /* domainGetSchedulerParameters */
    lxcSetSchedulerParameters, /* domainSetSchedulerParameters */
D
Daniel Veillard 已提交
1311 1312 1313 1314 1315
    NULL, /* domainMigratePrepare */
    NULL, /* domainMigratePerform */
    NULL, /* domainMigrateFinish */
    NULL, /* domainBlockStats */
    NULL, /* domainInterfaceStats */
D
Daniel P. Berrange 已提交
1316 1317
    NULL, /* domainBlockPeek */
    NULL, /* domainMemoryPeek */
D
Daniel Veillard 已提交
1318 1319
    NULL, /* nodeGetCellsFreeMemory */
    NULL, /* getFreeMemory */
1320 1321
    NULL, /* domainEventRegister */
    NULL, /* domainEventDeregister */
D
Daniel Veillard 已提交
1322 1323
    NULL, /* domainMigratePrepare2 */
    NULL, /* domainMigrateFinish2 */
D
Daniel Veillard 已提交
1324 1325
};

1326
static virStateDriver lxcStateDriver = {
1327 1328 1329
    .initialize = lxcStartup,
    .cleanup = lxcShutdown,
    .active = lxcActive,
1330 1331
};

D
Daniel Veillard 已提交
1332 1333 1334
int lxcRegister(void)
{
    virRegisterDriver(&lxcDriver);
1335
    virRegisterStateDriver(&lxcStateDriver);
D
Daniel Veillard 已提交
1336 1337
    return 0;
}