~ubuntu-branches/ubuntu/raring/libvirt/raring

« back to all changes in this revision

Viewing changes to tests/qemumonitortestutils.c

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-11-19 10:41:02 UTC
  • mfrom: (1.2.15) (223.1.2 raring-proposed)
  • Revision ID: package-import@ubuntu.com-20121119104102-l6ewdppikysbzztu
Tags: 1.0.0-0ubuntu2
debian/patches/add-armhf-sysinfo-infomration.patch: Disable
to fix FTBFS on arm.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011-2012 Red Hat, Inc.
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2.1 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library.  If not, see
 
16
 * <http://www.gnu.org/licenses/>.
 
17
 *
 
18
 */
 
19
 
 
20
#include <config.h>
 
21
 
 
22
#include <stdio.h>
 
23
#include <stdlib.h>
 
24
#include <string.h>
 
25
#include <time.h>
 
26
 
 
27
#include "qemumonitortestutils.h"
 
28
 
 
29
#include "threads.h"
 
30
#include "qemu/qemu_monitor.h"
 
31
#include "rpc/virnetsocket.h"
 
32
#include "memory.h"
 
33
#include "util.h"
 
34
#include "logging.h"
 
35
#include "virterror_internal.h"
 
36
 
 
37
#define VIR_FROM_THIS VIR_FROM_NONE
 
38
 
 
39
typedef struct _qemuMonitorTestItem qemuMonitorTestItem;
 
40
typedef qemuMonitorTestItem *qemuMonitorTestItemPtr;
 
41
 
 
42
struct _qemuMonitorTestItem {
 
43
    char *command_name;
 
44
    char *response;
 
45
};
 
46
 
 
47
struct _qemuMonitorTest {
 
48
    virMutex lock;
 
49
    virThread thread;
 
50
 
 
51
    bool json;
 
52
    bool quit;
 
53
    bool running;
 
54
 
 
55
    char *incoming;
 
56
    size_t incomingLength;
 
57
    size_t incomingCapacity;
 
58
 
 
59
    char *outgoing;
 
60
    size_t outgoingLength;
 
61
    size_t outgoingCapacity;
 
62
 
 
63
    virNetSocketPtr server;
 
64
    virNetSocketPtr client;
 
65
 
 
66
    qemuMonitorPtr mon;
 
67
 
 
68
    size_t nitems;
 
69
    qemuMonitorTestItemPtr *items;
 
70
 
 
71
    virDomainObjPtr vm;
 
72
};
 
73
 
 
74
 
 
75
static void qemuMonitorTestItemFree(qemuMonitorTestItemPtr item);
 
76
 
 
77
/*
 
78
 * Appends data for a reply onto the outgoing buffer
 
79
 */
 
80
static int qemuMonitorTestAddReponse(qemuMonitorTestPtr test,
 
81
                                     const char *response)
 
82
{
 
83
    size_t want = strlen(response) + 2;
 
84
    size_t have = test->outgoingCapacity - test->outgoingLength;
 
85
 
 
86
    if (have < want) {
 
87
        size_t need = want - have;
 
88
        if (VIR_EXPAND_N(test->outgoing, test->outgoingCapacity, need) < 0) {
 
89
            virReportOOMError();
 
90
            return -1;
 
91
        }
 
92
    }
 
93
 
 
94
    want -= 2;
 
95
    memcpy(test->outgoing + test->outgoingLength,
 
96
           response,
 
97
           want);
 
98
    memcpy(test->outgoing + test->outgoingLength + want,
 
99
           "\r\n",
 
100
           2);
 
101
    test->outgoingLength += want + 2;
 
102
    return 0;
 
103
}
 
104
 
 
105
 
 
106
/*
 
107
 * Processes a single line, looking for a matching expected
 
108
 * item to reply with, else replies with an error
 
109
 */
 
110
static int qemuMonitorTestProcessCommandJSON(qemuMonitorTestPtr test,
 
111
                                             const char *cmdstr)
 
112
{
 
113
    virJSONValuePtr val;
 
114
    const char *cmdname;
 
115
    int ret = -1;
 
116
 
 
117
    if (!(val = virJSONValueFromString(cmdstr)))
 
118
        return -1;
 
119
 
 
120
    if (!(cmdname = virJSONValueObjectGetString(val, "execute"))) {
 
121
        virReportError(VIR_ERR_INTERNAL_ERROR,
 
122
                       "Missing command name in %s", cmdstr);
 
123
        goto cleanup;
 
124
    }
 
125
 
 
126
    if (test->nitems == 0 ||
 
127
        STRNEQ(test->items[0]->command_name, cmdname)) {
 
128
        ret = qemuMonitorTestAddReponse(test,
 
129
                                        "{ \"error\": "
 
130
                                        " { \"desc\": \"Unexpected command\", "
 
131
                                        "   \"class\": \"UnexpectedCommand\" } }");
 
132
    } else {
 
133
        ret = qemuMonitorTestAddReponse(test,
 
134
                                        test->items[0]->response);
 
135
        qemuMonitorTestItemFree(test->items[0]);
 
136
        if (test->nitems == 1) {
 
137
            VIR_FREE(test->items);
 
138
            test->nitems = 0;
 
139
        } else {
 
140
            memmove(test->items,
 
141
                    test->items + 1,
 
142
                    sizeof(test->items[0]) * (test->nitems - 1));
 
143
            VIR_SHRINK_N(test->items, test->nitems, 1);
 
144
        }
 
145
    }
 
146
 
 
147
cleanup:
 
148
    virJSONValueFree(val);
 
149
    return ret;
 
150
}
 
151
 
 
152
 
 
153
static int qemuMonitorTestProcessCommandText(qemuMonitorTestPtr test,
 
154
                                             const char *cmdstr)
 
155
{
 
156
    char *tmp;
 
157
    char *cmdname;
 
158
    int ret = -1;
 
159
 
 
160
    if (!(cmdname = strdup(cmdstr))) {
 
161
        virReportOOMError();
 
162
        return -1;
 
163
    }
 
164
    if (!(tmp = strchr(cmdname, ' '))) {
 
165
        virReportError(VIR_ERR_INTERNAL_ERROR,
 
166
                       "Cannot find command name in '%s'", cmdstr);
 
167
        goto cleanup;
 
168
    }
 
169
    *tmp = '\0';
 
170
 
 
171
    if (test->nitems == 0 ||
 
172
        STRNEQ(test->items[0]->command_name, cmdname)) {
 
173
        ret = qemuMonitorTestAddReponse(test,
 
174
                                        "unexpected command");
 
175
    } else {
 
176
        ret = qemuMonitorTestAddReponse(test,
 
177
                                        test->items[0]->response);
 
178
        qemuMonitorTestItemFree(test->items[0]);
 
179
        if (test->nitems == 1) {
 
180
            VIR_FREE(test->items);
 
181
            test->nitems = 0;
 
182
        } else {
 
183
            memmove(test->items,
 
184
                    test->items + 1,
 
185
                    sizeof(test->items[0]) * (test->nitems - 1));
 
186
            VIR_SHRINK_N(test->items, test->nitems, 1);
 
187
        }
 
188
    }
 
189
 
 
190
cleanup:
 
191
    VIR_FREE(cmdname);
 
192
    return ret;
 
193
}
 
194
 
 
195
static int qemuMonitorTestProcessCommand(qemuMonitorTestPtr test,
 
196
                                         const char *cmdstr)
 
197
{
 
198
    if (test->json)
 
199
        return qemuMonitorTestProcessCommandJSON(test ,cmdstr);
 
200
    else
 
201
        return qemuMonitorTestProcessCommandText(test ,cmdstr);
 
202
}
 
203
 
 
204
/*
 
205
 * Handles read/write of monitor data on the monitor server side
 
206
 */
 
207
static void qemuMonitorTestIO(virNetSocketPtr sock,
 
208
                              int events,
 
209
                              void *opaque)
 
210
{
 
211
    qemuMonitorTestPtr test = opaque;
 
212
    bool err = false;
 
213
 
 
214
    virMutexLock(&test->lock);
 
215
    if (events & VIR_EVENT_HANDLE_WRITABLE) {
 
216
        ssize_t ret;
 
217
        if ((ret = virNetSocketWrite(sock,
 
218
                                     test->outgoing,
 
219
                                     test->outgoingLength)) < 0) {
 
220
            err = true;
 
221
            goto cleanup;
 
222
        }
 
223
 
 
224
        memmove(test->outgoing,
 
225
                test->outgoing + ret,
 
226
                test->outgoingLength - ret);
 
227
        test->outgoingLength -= ret;
 
228
 
 
229
        if ((test->outgoingCapacity - test->outgoingLength) > 1024)
 
230
            VIR_SHRINK_N(test->outgoing, test->outgoingCapacity, 1024);
 
231
    }
 
232
 
 
233
    if (events & VIR_EVENT_HANDLE_READABLE) {
 
234
        ssize_t ret, used;
 
235
        char *t1, *t2;
 
236
 
 
237
        if ((test->incomingCapacity - test->incomingLength) < 1024) {
 
238
            if (VIR_EXPAND_N(test->incoming, test->incomingCapacity, 1024) < 0) {
 
239
                err = true;
 
240
                goto cleanup;
 
241
            }
 
242
        }
 
243
 
 
244
        if ((ret = virNetSocketRead(sock,
 
245
                                    test->incoming + test->incomingLength,
 
246
                                    (test->incomingCapacity - test->incomingLength) - 1)) < 0) {
 
247
            err = true;
 
248
            goto cleanup;
 
249
        }
 
250
        test->incomingLength += ret;
 
251
        test->incoming[test->incomingLength] = '\0';
 
252
 
 
253
        /* Look to see if we've got a complete line, and
 
254
         * if so, handle that command
 
255
         */
 
256
        t1 = test->incoming;
 
257
        while ((t2 = strstr(t1, "\r\n"))) {
 
258
            *t2 = '\0';
 
259
 
 
260
            if (qemuMonitorTestProcessCommand(test, t1) < 0) {
 
261
                err = true;
 
262
                goto cleanup;
 
263
            }
 
264
 
 
265
            t1 = t2 + 2;
 
266
        }
 
267
        used = t1 - test->incoming;
 
268
        memmove(test->incoming, t1, test->incomingLength - used);
 
269
        test->incomingLength -= used;
 
270
        if ((test->incomingCapacity - test->incomingLength) > 1024) {
 
271
            VIR_SHRINK_N(test->incoming,
 
272
                         test->incomingCapacity,
 
273
                         1024);
 
274
        }
 
275
    }
 
276
 
 
277
    if (events & (VIR_EVENT_HANDLE_HANGUP |
 
278
                  VIR_EVENT_HANDLE_ERROR))
 
279
        err = true;
 
280
 
 
281
cleanup:
 
282
    if (err) {
 
283
        virNetSocketRemoveIOCallback(sock);
 
284
        virNetSocketClose(sock);
 
285
        virObjectUnref(test->client);
 
286
        test->client = NULL;
 
287
    } else {
 
288
        events = VIR_EVENT_HANDLE_READABLE;
 
289
 
 
290
        if (test->outgoingLength)
 
291
            events |= VIR_EVENT_HANDLE_WRITABLE;
 
292
 
 
293
        virNetSocketUpdateIOCallback(sock, events);
 
294
    }
 
295
    virMutexUnlock(&test->lock);
 
296
}
 
297
 
 
298
 
 
299
static void qemuMonitorTestWorker(void *opaque)
 
300
{
 
301
    qemuMonitorTestPtr test = opaque;
 
302
 
 
303
    virMutexLock(&test->lock);
 
304
 
 
305
    while (!test->quit) {
 
306
        virMutexUnlock(&test->lock);
 
307
 
 
308
        if (virEventRunDefaultImpl() < 0) {
 
309
            test->quit = true;
 
310
            break;
 
311
        }
 
312
 
 
313
        virMutexLock(&test->lock);
 
314
    }
 
315
 
 
316
    test->running = false;
 
317
 
 
318
    virMutexUnlock(&test->lock);
 
319
    return;
 
320
}
 
321
 
 
322
static void qemuMonitorTestItemFree(qemuMonitorTestItemPtr item)
 
323
{
 
324
    if (!item)
 
325
        return;
 
326
 
 
327
    VIR_FREE(item->command_name);
 
328
    VIR_FREE(item->response);
 
329
 
 
330
    VIR_FREE(item);
 
331
}
 
332
 
 
333
 
 
334
void qemuMonitorTestFree(qemuMonitorTestPtr test)
 
335
{
 
336
    size_t i;
 
337
 
 
338
    if (!test)
 
339
        return;
 
340
 
 
341
    virMutexLock(&test->lock);
 
342
    if (test->running) {
 
343
        test->quit = true;
 
344
    }
 
345
    virMutexUnlock(&test->lock);
 
346
 
 
347
    if (test->client) {
 
348
        virNetSocketRemoveIOCallback(test->client);
 
349
        virNetSocketClose(test->client);
 
350
        virObjectUnref(test->client);
 
351
    }
 
352
 
 
353
    virObjectUnref(test->server);
 
354
    if (test->mon) {
 
355
        qemuMonitorUnlock(test->mon);
 
356
        qemuMonitorClose(test->mon);
 
357
    }
 
358
 
 
359
    virObjectUnref(test->vm);
 
360
 
 
361
    if (test->running)
 
362
        virThreadJoin(&test->thread);
 
363
 
 
364
    for (i = 0 ; i < test->nitems ; i++)
 
365
        qemuMonitorTestItemFree(test->items[i]);
 
366
    VIR_FREE(test->items);
 
367
 
 
368
    virMutexDestroy(&test->lock);
 
369
    VIR_FREE(test);
 
370
}
 
371
 
 
372
 
 
373
int
 
374
qemuMonitorTestAddItem(qemuMonitorTestPtr test,
 
375
                       const char *command_name,
 
376
                       const char *response)
 
377
{
 
378
    qemuMonitorTestItemPtr item;
 
379
 
 
380
    if (VIR_ALLOC(item) < 0)
 
381
        goto no_memory;
 
382
 
 
383
    if (!(item->command_name = strdup(command_name)) ||
 
384
        !(item->response = strdup(response)))
 
385
        goto no_memory;
 
386
 
 
387
    virMutexLock(&test->lock);
 
388
    if (VIR_EXPAND_N(test->items, test->nitems, 1) < 0) {
 
389
        virMutexUnlock(&test->lock);
 
390
        goto no_memory;
 
391
    }
 
392
    test->items[test->nitems - 1] = item;
 
393
 
 
394
    virMutexUnlock(&test->lock);
 
395
 
 
396
    return 0;
 
397
 
 
398
no_memory:
 
399
    virReportOOMError();
 
400
    qemuMonitorTestItemFree(item);
 
401
    return -1;
 
402
}
 
403
 
 
404
 
 
405
static void qemuMonitorTestEOFNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
 
406
                                         virDomainObjPtr vm ATTRIBUTE_UNUSED)
 
407
{
 
408
}
 
409
 
 
410
static void qemuMonitorTestErrorNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
 
411
                                           virDomainObjPtr vm ATTRIBUTE_UNUSED)
 
412
{
 
413
}
 
414
 
 
415
 
 
416
static qemuMonitorCallbacks qemuCallbacks = {
 
417
    .eofNotify = qemuMonitorTestEOFNotify,
 
418
    .errorNotify = qemuMonitorTestErrorNotify,
 
419
};
 
420
 
 
421
#define QEMU_JSON_GREETING "{\"QMP\": {\"version\": {\"qemu\": {\"micro\": 1, \"minor\": 0, \"major\": 1}, \"package\": \" (qemu-kvm-1.0.1)\"}, \"capabilities\": []}}"
 
422
#define QEMU_TEXT_GREETING "QEMU 1.0,1 monitor - type 'help' for more information"
 
423
 
 
424
qemuMonitorTestPtr qemuMonitorTestNew(bool json, virCapsPtr caps)
 
425
{
 
426
    qemuMonitorTestPtr test = NULL;
 
427
    virDomainChrSourceDef src;
 
428
 
 
429
    char *tmpdir = NULL, *path = NULL;
 
430
    char template[] = "/tmp/libvirt_XXXXXX";
 
431
 
 
432
    tmpdir = mkdtemp(template);
 
433
    if (tmpdir == NULL) {
 
434
        virReportSystemError(errno, "%s",
 
435
                             "Failed to create temporary directory");
 
436
        goto error;
 
437
    }
 
438
 
 
439
    if (virAsprintf(&path, "%s/qemumonitorjsontest.sock", tmpdir) < 0) {
 
440
        virReportOOMError();
 
441
        goto error;
 
442
    }
 
443
 
 
444
    memset(&src, 0, sizeof(src));
 
445
    src.type = VIR_DOMAIN_CHR_TYPE_UNIX;
 
446
    src.data.nix.path = (char *)path;
 
447
    src.data.nix.listen = false;
 
448
 
 
449
    if (VIR_ALLOC(test) < 0) {
 
450
        virReportOOMError();
 
451
        return NULL;
 
452
    }
 
453
 
 
454
    if (virMutexInit(&test->lock) < 0) {
 
455
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
 
456
                       "Cannot initialize mutex");
 
457
        VIR_FREE(test);
 
458
        return NULL;
 
459
    }
 
460
 
 
461
    test->json = json;
 
462
    if (!(test->vm = virDomainObjNew(caps)))
 
463
        goto error;
 
464
 
 
465
    if (virNetSocketNewListenUNIX(path,
 
466
                                  0700,
 
467
                                  getuid(),
 
468
                                  getgid(),
 
469
                                  &test->server) < 0)
 
470
        goto error;
 
471
 
 
472
 
 
473
    if (virNetSocketListen(test->server, 1) < 0)
 
474
        goto error;
 
475
 
 
476
    if (!(test->mon = qemuMonitorOpen(test->vm,
 
477
                                      &src,
 
478
                                      json ? 1 : 0,
 
479
                                      &qemuCallbacks)))
 
480
        goto error;
 
481
    qemuMonitorLock(test->mon);
 
482
 
 
483
    if (virNetSocketAccept(test->server, &test->client) < 0)
 
484
        goto error;
 
485
    if (!test->client)
 
486
        goto error;
 
487
 
 
488
    if (qemuMonitorTestAddReponse(test, json ?
 
489
                                  QEMU_JSON_GREETING :
 
490
                                  QEMU_TEXT_GREETING) < 0)
 
491
        goto error;
 
492
 
 
493
    if (virNetSocketAddIOCallback(test->client,
 
494
                                  VIR_EVENT_HANDLE_WRITABLE,
 
495
                                  qemuMonitorTestIO,
 
496
                                  test,
 
497
                                  NULL) < 0)
 
498
        goto error;
 
499
 
 
500
    virMutexLock(&test->lock);
 
501
    if (virThreadCreate(&test->thread,
 
502
                        true,
 
503
                        qemuMonitorTestWorker,
 
504
                        test) < 0) {
 
505
        virMutexUnlock(&test->lock);
 
506
        goto error;
 
507
    }
 
508
    test->running = true;
 
509
    virMutexUnlock(&test->lock);
 
510
 
 
511
cleanup:
 
512
    if (tmpdir)
 
513
        if (rmdir(tmpdir) < 0)
 
514
            VIR_WARN("Failed to remove tempdir: %s", strerror(errno));
 
515
    VIR_FREE(path);
 
516
    return test;
 
517
 
 
518
error:
 
519
    qemuMonitorTestFree(test);
 
520
    goto cleanup;
 
521
}
 
522
 
 
523
qemuMonitorPtr qemuMonitorTestGetMonitor(qemuMonitorTestPtr test)
 
524
{
 
525
    return test->mon;
 
526
}