event-test.c 22.1 KB
Newer Older
1
#include <config.h>
2

3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <string.h>
6
#include <signal.h>
7
#include <inttypes.h>
8

9 10
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
11

12
#define VIR_DEBUG(fmt) printf("%s:%d: " fmt "\n", __func__, __LINE__)
13
#define STREQ(a,b) (strcmp(a,b) == 0)
14

15 16 17
#ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__((__unused__))
#endif
18

19 20
int run = 1;

21 22 23
/* Prototypes */
const char *eventToString(int event);
int myEventAddHandleFunc  (int fd, int event,
24 25 26
                           virEventHandleCallback cb,
                           void *opaque,
                           virFreeCallback ff);
27 28
void myEventUpdateHandleFunc(int watch, int event);
int  myEventRemoveHandleFunc(int watch);
29

30 31 32 33
int myEventAddTimeoutFunc(int timeout,
                          virEventTimeoutCallback cb,
                          void *opaque,
                          virFreeCallback ff);
34 35 36 37 38 39 40 41 42 43
void myEventUpdateTimeoutFunc(int timer, int timout);
int myEventRemoveTimeoutFunc(int timer);

int myEventHandleTypeToPollEvent(virEventHandleType events);
virEventHandleType myPollEventToEventHandleType(int events);

void usage(const char *pname);

/* Callback functions */

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

static void connectClose(virConnectPtr conn ATTRIBUTE_UNUSED,
                         int reason,
                         void *opaque ATTRIBUTE_UNUSED)
{
    switch (reason) {
    case VIR_CONNECT_CLOSE_REASON_ERROR:
        fprintf(stderr, "Connection closed due to I/O error\n");
        break;
    case VIR_CONNECT_CLOSE_REASON_EOF:
        fprintf(stderr, "Connection closed due to end of file\n");
        break;
    case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
        fprintf(stderr, "Connection closed due to keepalive timeout\n");
        break;
    case VIR_CONNECT_CLOSE_REASON_CLIENT:
        fprintf(stderr, "Connection closed due to client request\n");
        break;
    default:
        fprintf(stderr, "Connection closed due to unknown reason\n");
        break;
    };
    run = 0;
}

69
const char *eventToString(int event) {
70
    const char *ret = "";
71
    switch ((virDomainEventType) event) {
72 73
        case VIR_DOMAIN_EVENT_DEFINED:
            ret ="Defined";
74
            break;
75 76
        case VIR_DOMAIN_EVENT_UNDEFINED:
            ret ="Undefined";
77 78 79 80 81 82 83 84 85 86 87 88 89
            break;
        case VIR_DOMAIN_EVENT_STARTED:
            ret ="Started";
            break;
        case VIR_DOMAIN_EVENT_SUSPENDED:
            ret ="Suspended";
            break;
        case VIR_DOMAIN_EVENT_RESUMED:
            ret ="Resumed";
            break;
        case VIR_DOMAIN_EVENT_STOPPED:
            ret ="Stopped";
            break;
90 91 92
        case VIR_DOMAIN_EVENT_SHUTDOWN:
            ret = "Shutdown";
            break;
93 94 95 96 97 98
    }
    return ret;
}

static const char *eventDetailToString(int event, int detail) {
    const char *ret = "";
99
    switch ((virDomainEventType) event) {
100 101 102 103 104 105 106 107 108
        case VIR_DOMAIN_EVENT_DEFINED:
            if (detail == VIR_DOMAIN_EVENT_DEFINED_ADDED)
                ret = "Added";
            else if (detail == VIR_DOMAIN_EVENT_DEFINED_UPDATED)
                ret = "Updated";
            break;
        case VIR_DOMAIN_EVENT_UNDEFINED:
            if (detail == VIR_DOMAIN_EVENT_UNDEFINED_REMOVED)
                ret = "Removed";
109
            break;
110
        case VIR_DOMAIN_EVENT_STARTED:
111
            switch ((virDomainEventStartedDetailType) detail) {
112 113 114 115 116 117 118 119 120
            case VIR_DOMAIN_EVENT_STARTED_BOOTED:
                ret = "Booted";
                break;
            case VIR_DOMAIN_EVENT_STARTED_MIGRATED:
                ret = "Migrated";
                break;
            case VIR_DOMAIN_EVENT_STARTED_RESTORED:
                ret = "Restored";
                break;
121 122 123
            case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT:
                ret = "Snapshot";
                break;
124 125 126
            case VIR_DOMAIN_EVENT_STARTED_WAKEUP:
                ret = "Event wakeup";
                break;
127 128 129
            }
            break;
        case VIR_DOMAIN_EVENT_SUSPENDED:
130
            switch ((virDomainEventSuspendedDetailType) detail) {
131
            case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED:
132
                ret = "Paused";
133 134
                break;
            case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED:
135
                ret = "Migrated";
136 137 138 139 140 141 142 143 144 145 146 147 148 149
                break;
            case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR:
                ret = "I/O Error";
                break;
            case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG:
                ret = "Watchdog";
                break;
            case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED:
                ret = "Restored";
                break;
            case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT:
                ret = "Snapshot";
                break;
            }
150 151
            break;
        case VIR_DOMAIN_EVENT_RESUMED:
152
            switch ((virDomainEventResumedDetailType) detail) {
153
            case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED:
154
                ret = "Unpaused";
155 156
                break;
            case VIR_DOMAIN_EVENT_RESUMED_MIGRATED:
157
                ret = "Migrated";
158 159 160 161 162
                break;
            case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT:
                ret = "Snapshot";
                break;
            }
163 164
            break;
        case VIR_DOMAIN_EVENT_STOPPED:
165
            switch ((virDomainEventStoppedDetailType) detail) {
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
            case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN:
                ret = "Shutdown";
                break;
            case VIR_DOMAIN_EVENT_STOPPED_DESTROYED:
                ret = "Destroyed";
                break;
            case VIR_DOMAIN_EVENT_STOPPED_CRASHED:
                ret = "Crashed";
                break;
            case VIR_DOMAIN_EVENT_STOPPED_MIGRATED:
                ret = "Migrated";
                break;
            case VIR_DOMAIN_EVENT_STOPPED_SAVED:
                ret = "Failed";
                break;
            case VIR_DOMAIN_EVENT_STOPPED_FAILED:
                ret = "Failed";
                break;
184 185 186
            case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT:
                ret = "Snapshot";
                break;
187
            }
188
            break;
189 190 191 192 193 194 195
        case VIR_DOMAIN_EVENT_SHUTDOWN:
            switch ((virDomainEventShutdownDetailType) detail) {
            case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED:
                ret = "Finished";
                break;
            }
            break;
196 197 198 199
    }
    return ret;
}

200 201 202 203 204
static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED,
                                  virDomainPtr dom,
                                  int event,
                                  int detail,
                                  void *opaque ATTRIBUTE_UNUSED)
205
{
206
    printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom),
207 208
           virDomainGetID(dom), eventToString(event),
           eventDetailToString(event, detail));
209 210 211
    return 0;
}

212 213 214 215 216
static int myDomainEventCallback2(virConnectPtr conn ATTRIBUTE_UNUSED,
                                  virDomainPtr dom,
                                  int event,
                                  int detail,
                                  void *opaque ATTRIBUTE_UNUSED)
217
{
218
    printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom),
219 220
           virDomainGetID(dom), eventToString(event),
           eventDetailToString(event, detail));
221 222 223
    return 0;
}

224 225 226 227 228 229 230 231 232 233
static int myDomainEventRebootCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                       virDomainPtr dom,
                                       void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) rebooted\n", __func__, virDomainGetName(dom),
           virDomainGetID(dom));

    return 0;
}

234 235 236 237 238
static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                          virDomainPtr dom,
                                          long long offset,
                                          void *opaque ATTRIBUTE_UNUSED)
{
239 240 241 242 243 244 245 246 247 248
    char *str = NULL;
    /* HACK: use asprintf since we have gnulib's wrapper for %lld on Win32
     * but don't have a printf() replacement with %lld */
    if (asprintf(&str, "%s EVENT: Domain %s(%d) rtc change %lld\n",
                 __func__, virDomainGetName(dom),
                 virDomainGetID(dom), offset) < 0)
        return 0;

    printf("%s", str);
    free(str);
249 250 251 252

    return 0;
}

253 254 255 256 257 258 259 260 261 262 263
static int myDomainEventBalloonChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                              virDomainPtr dom,
                                              unsigned long long actual,
                                              void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) balloon change %" PRIuMAX "KB\n",
           __func__, virDomainGetName(dom), virDomainGetID(dom), (uintmax_t)actual);

    return 0;
}

264 265 266 267 268 269 270 271 272 273 274
static int myDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                         virDomainPtr dom,
                                         int action,
                                         void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) watchdog action=%d\n", __func__, virDomainGetName(dom),
           virDomainGetID(dom), action);

    return 0;
}

275 276 277 278 279 280 281 282 283 284 285 286 287
static int myDomainEventIOErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                        virDomainPtr dom,
                                        const char *srcPath,
                                        const char *devAlias,
                                        int action,
                                        void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) io error path=%s alias=%s action=%d\n", __func__, virDomainGetName(dom),
           virDomainGetID(dom), srcPath, devAlias, action);

    return 0;
}

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
static int myDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                         virDomainPtr dom,
                                         int phase,
                                         virDomainEventGraphicsAddressPtr local,
                                         virDomainEventGraphicsAddressPtr remote,
                                         const char *authScheme,
                                         virDomainEventGraphicsSubjectPtr subject,
                                         void *opaque ATTRIBUTE_UNUSED)
{
    int i;
    printf("%s EVENT: Domain %s(%d) graphics ", __func__, virDomainGetName(dom),
           virDomainGetID(dom));

    switch (phase) {
    case VIR_DOMAIN_EVENT_GRAPHICS_CONNECT:
        printf("connected ");
        break;
    case VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE:
        printf("initialized ");
        break;
    case VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT:
        printf("disconnected ");
        break;
    }

    printf("local: family=%d node=%s service=%s ",
           local->family, local->node, local->service);
    printf("remote: family=%d node=%s service=%s ",
           remote->family, remote->node, remote->service);

    printf("auth: %s ", authScheme);
    for (i = 0 ; i < subject->nidentity ; i++) {
        printf(" identity: %s=%s",
               subject->identities[i].type,
               subject->identities[i].name);
    }
    printf("\n");

    return 0;
}

329 330 331 332 333 334 335 336 337 338 339
static int myDomainEventControlErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                             virDomainPtr dom,
                                             void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) control error\n", __func__, virDomainGetName(dom),
           virDomainGetID(dom));

    return 0;
}


340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
const char *diskChangeReasonStrings[] = {
    "startupPolicy", /* 0 */
    /* add new reason here */
};
static int myDomainEventDiskChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                           virDomainPtr dom,
                                           const char *oldSrcPath,
                                           const char *newSrcPath,
                                           const char *devAlias,
                                           int reason,
                                           void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) disk change oldSrcPath: %s newSrcPath: %s devAlias: %s reason: %s\n",
           __func__, virDomainGetName(dom), virDomainGetID(dom),
           oldSrcPath, newSrcPath, devAlias, diskChangeReasonStrings[reason]);
    return 0;
}

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
const char *trayChangeReasonStrings[] = {
    "open",
    "close",
};

static int myDomainEventTrayChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                           virDomainPtr dom,
                                           const char *devAlias,
                                           int reason,
                                           void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) removable disk's tray change devAlias: %s reason: %s\n",
           __func__, virDomainGetName(dom), virDomainGetID(dom),
           devAlias, trayChangeReasonStrings[reason]);
    return 0;
}
374

O
Osier Yang 已提交
375 376 377 378 379 380 381 382 383 384
static int myDomainEventPMWakeupCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                         virDomainPtr dom,
                                         int reason ATTRIBUTE_UNUSED,
                                         void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) system pmwakeup",
           __func__, virDomainGetName(dom), virDomainGetID(dom));
    return 0;
}

O
Osier Yang 已提交
385 386 387 388 389 390 391 392 393 394
static int myDomainEventPMSuspendCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                          virDomainPtr dom,
                                          int reason ATTRIBUTE_UNUSED,
                                          void *opaque ATTRIBUTE_UNUSED)
{
    printf("%s EVENT: Domain %s(%d) system pmsuspend",
           __func__, virDomainGetName(dom), virDomainGetID(dom));
    return 0;
}

395 396 397
static void myFreeFunc(void *opaque)
{
    char *str = opaque;
398
    printf("%s: Freeing [%s]\n", __func__, str);
399 400 401
    free(str);
}

402 403 404 405 406 407 408 409

/* main test functions */

void usage(const char *pname)
{
    printf("%s uri\n", pname);
}

410 411 412 413 414 415 416 417

static void stop(int sig)
{
    printf("Exiting on signal %d\n", sig);
    run = 0;
}


418 419
int main(int argc, char **argv)
{
420 421
    int callback1ret = -1;
    int callback2ret = -1;
422
    int callback3ret = -1;
423
    int callback4ret = -1;
424
    int callback5ret = -1;
425
    int callback6ret = -1;
426
    int callback7ret = -1;
427
    int callback8ret = -1;
428
    int callback9ret = -1;
429
    int callback10ret = -1;
O
Osier Yang 已提交
430
    int callback11ret = -1;
O
Osier Yang 已提交
431
    int callback12ret = -1;
432
    int callback13ret = -1;
433
    struct sigaction action_stop;
434

435
    memset(&action_stop, 0, sizeof(action_stop));
436 437

    action_stop.sa_handler = stop;
438

439
    if (argc > 1 && STREQ(argv[1], "--help")) {
440 441 442
        usage(argv[0]);
        return -1;
    }
443

444
    virEventRegisterDefaultImpl();
445 446

    virConnectPtr dconn = NULL;
447 448 449
    dconn = virConnectOpenAuth(argc > 1 ? argv[1] : NULL,
                               virConnectAuthPtrDefault,
                               VIR_CONNECT_RO);
450 451 452 453 454
    if (!dconn) {
        printf("error opening\n");
        return -1;
    }

455 456 457
    virConnectRegisterCloseCallback(dconn,
                                    connectClose, NULL, NULL);

458 459 460
    sigaction(SIGTERM, &action_stop, NULL);
    sigaction(SIGINT, &action_stop, NULL);

461
    VIR_DEBUG("Registering domain event cbs");
462 463

    /* Add 2 callbacks to prove this works with more than just one */
464 465
    callback1ret = virConnectDomainEventRegister(dconn, myDomainEventCallback1,
                                                 strdup("callback 1"), myFreeFunc);
466 467 468 469 470
    callback2ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_LIFECYCLE,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventCallback2),
                                                    strdup("callback 2"), myFreeFunc);
471 472 473 474 475
    callback3ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_REBOOT,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRebootCallback),
                                                    strdup("callback reboot"), myFreeFunc);
476 477 478 479 480
    callback4ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_RTC_CHANGE,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRTCChangeCallback),
                                                    strdup("callback rtcchange"), myFreeFunc);
481 482 483 484 485
    callback5ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_WATCHDOG,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventWatchdogCallback),
                                                    strdup("callback watchdog"), myFreeFunc);
486 487 488 489 490
    callback6ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_IO_ERROR,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventIOErrorCallback),
                                                    strdup("callback io error"), myFreeFunc);
491 492 493 494 495
    callback7ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_GRAPHICS,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventGraphicsCallback),
                                                    strdup("callback graphics"), myFreeFunc);
496 497 498 499 500
    callback8ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_CONTROL_ERROR,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventControlErrorCallback),
                                                    strdup("callback control error"), myFreeFunc);
501 502 503 504 505
    callback9ret = virConnectDomainEventRegisterAny(dconn,
                                                    NULL,
                                                    VIR_DOMAIN_EVENT_ID_DISK_CHANGE,
                                                    VIR_DOMAIN_EVENT_CALLBACK(myDomainEventDiskChangeCallback),
                                                    strdup("disk change"), myFreeFunc);
506 507 508 509 510
    callback10ret = virConnectDomainEventRegisterAny(dconn,
                                                     NULL,
                                                     VIR_DOMAIN_EVENT_ID_TRAY_CHANGE,
                                                     VIR_DOMAIN_EVENT_CALLBACK(myDomainEventTrayChangeCallback),
                                                     strdup("tray change"), myFreeFunc);
O
Osier Yang 已提交
511 512 513 514 515
    callback11ret = virConnectDomainEventRegisterAny(dconn,
                                                     NULL,
                                                     VIR_DOMAIN_EVENT_ID_PMWAKEUP,
                                                     VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMWakeupCallback),
                                                     strdup("pmwakeup"), myFreeFunc);
O
Osier Yang 已提交
516 517 518 519 520
    callback12ret = virConnectDomainEventRegisterAny(dconn,
                                                     NULL,
                                                     VIR_DOMAIN_EVENT_ID_PMSUSPEND,
                                                     VIR_DOMAIN_EVENT_CALLBACK(myDomainEventPMSuspendCallback),
                                                     strdup("pmsuspend"), myFreeFunc);
521 522 523 524 525
    callback13ret = virConnectDomainEventRegisterAny(dconn,
                                                     NULL,
                                                     VIR_DOMAIN_EVENT_ID_BALLOON_CHANGE,
                                                     VIR_DOMAIN_EVENT_CALLBACK(myDomainEventBalloonChangeCallback),
                                                     strdup("callback balloonchange"), myFreeFunc);
526
    if ((callback1ret != -1) &&
527
        (callback2ret != -1) &&
528
        (callback3ret != -1) &&
529
        (callback4ret != -1) &&
530
        (callback5ret != -1) &&
531
        (callback6ret != -1) &&
532
        (callback7ret != -1) &&
533
        (callback9ret != -1) &&
O
Osier Yang 已提交
534
        (callback10ret != -1) &&
O
Osier Yang 已提交
535
        (callback11ret != -1) &&
536 537
        (callback12ret != -1) &&
        (callback13ret != -1)) {
538 539 540 541 542 543 544
        if (virConnectSetKeepAlive(dconn, 5, 3) < 0) {
            virErrorPtr err = virGetLastError();
            fprintf(stderr, "Failed to start keepalive protocol: %s\n",
                    err && err->message ? err->message : "Unknown error");
            run = 0;
        }

545
        while (run) {
546 547 548 549
            if (virEventRunDefaultImpl() < 0) {
                virErrorPtr err = virGetLastError();
                fprintf(stderr, "Failed to run event loop: %s\n",
                        err && err->message ? err->message : "Unknown error");
550
            }
551 552
        }

553
        VIR_DEBUG("Deregistering event handlers");
554
        virConnectDomainEventDeregister(dconn, myDomainEventCallback1);
555
        virConnectDomainEventDeregisterAny(dconn, callback2ret);
556
        virConnectDomainEventDeregisterAny(dconn, callback3ret);
557
        virConnectDomainEventDeregisterAny(dconn, callback4ret);
558
        virConnectDomainEventDeregisterAny(dconn, callback5ret);
559
        virConnectDomainEventDeregisterAny(dconn, callback6ret);
560
        virConnectDomainEventDeregisterAny(dconn, callback7ret);
561
        virConnectDomainEventDeregisterAny(dconn, callback9ret);
562
        virConnectDomainEventDeregisterAny(dconn, callback10ret);
O
Osier Yang 已提交
563
        virConnectDomainEventDeregisterAny(dconn, callback11ret);
O
Osier Yang 已提交
564
        virConnectDomainEventDeregisterAny(dconn, callback12ret);
565
        virConnectDomainEventDeregisterAny(dconn, callback13ret);
566 567
        if (callback8ret != -1)
            virConnectDomainEventDeregisterAny(dconn, callback8ret);
568 569
    }

570
    VIR_DEBUG("Closing connection");
571
    if (dconn && virConnectClose(dconn) < 0) {
572 573
        printf("error closing\n");
    }
574

575 576 577
    printf("done\n");
    return 0;
}