2
* Copyright (C) 2011-2012 Red Hat, Inc.
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.
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.
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/>.
27
#include "qemumonitortestutils.h"
30
#include "qemu/qemu_monitor.h"
31
#include "rpc/virnetsocket.h"
35
#include "virterror_internal.h"
37
#define VIR_FROM_THIS VIR_FROM_NONE
39
typedef struct _qemuMonitorTestItem qemuMonitorTestItem;
40
typedef qemuMonitorTestItem *qemuMonitorTestItemPtr;
42
struct _qemuMonitorTestItem {
47
struct _qemuMonitorTest {
56
size_t incomingLength;
57
size_t incomingCapacity;
60
size_t outgoingLength;
61
size_t outgoingCapacity;
63
virNetSocketPtr server;
64
virNetSocketPtr client;
69
qemuMonitorTestItemPtr *items;
75
static void qemuMonitorTestItemFree(qemuMonitorTestItemPtr item);
78
* Appends data for a reply onto the outgoing buffer
80
static int qemuMonitorTestAddReponse(qemuMonitorTestPtr test,
83
size_t want = strlen(response) + 2;
84
size_t have = test->outgoingCapacity - test->outgoingLength;
87
size_t need = want - have;
88
if (VIR_EXPAND_N(test->outgoing, test->outgoingCapacity, need) < 0) {
95
memcpy(test->outgoing + test->outgoingLength,
98
memcpy(test->outgoing + test->outgoingLength + want,
101
test->outgoingLength += want + 2;
107
* Processes a single line, looking for a matching expected
108
* item to reply with, else replies with an error
110
static int qemuMonitorTestProcessCommandJSON(qemuMonitorTestPtr test,
117
if (!(val = virJSONValueFromString(cmdstr)))
120
if (!(cmdname = virJSONValueObjectGetString(val, "execute"))) {
121
virReportError(VIR_ERR_INTERNAL_ERROR,
122
"Missing command name in %s", cmdstr);
126
if (test->nitems == 0 ||
127
STRNEQ(test->items[0]->command_name, cmdname)) {
128
ret = qemuMonitorTestAddReponse(test,
130
" { \"desc\": \"Unexpected command\", "
131
" \"class\": \"UnexpectedCommand\" } }");
133
ret = qemuMonitorTestAddReponse(test,
134
test->items[0]->response);
135
qemuMonitorTestItemFree(test->items[0]);
136
if (test->nitems == 1) {
137
VIR_FREE(test->items);
142
sizeof(test->items[0]) * (test->nitems - 1));
143
VIR_SHRINK_N(test->items, test->nitems, 1);
148
virJSONValueFree(val);
153
static int qemuMonitorTestProcessCommandText(qemuMonitorTestPtr test,
160
if (!(cmdname = strdup(cmdstr))) {
164
if (!(tmp = strchr(cmdname, ' '))) {
165
virReportError(VIR_ERR_INTERNAL_ERROR,
166
"Cannot find command name in '%s'", cmdstr);
171
if (test->nitems == 0 ||
172
STRNEQ(test->items[0]->command_name, cmdname)) {
173
ret = qemuMonitorTestAddReponse(test,
174
"unexpected command");
176
ret = qemuMonitorTestAddReponse(test,
177
test->items[0]->response);
178
qemuMonitorTestItemFree(test->items[0]);
179
if (test->nitems == 1) {
180
VIR_FREE(test->items);
185
sizeof(test->items[0]) * (test->nitems - 1));
186
VIR_SHRINK_N(test->items, test->nitems, 1);
195
static int qemuMonitorTestProcessCommand(qemuMonitorTestPtr test,
199
return qemuMonitorTestProcessCommandJSON(test ,cmdstr);
201
return qemuMonitorTestProcessCommandText(test ,cmdstr);
205
* Handles read/write of monitor data on the monitor server side
207
static void qemuMonitorTestIO(virNetSocketPtr sock,
211
qemuMonitorTestPtr test = opaque;
214
virMutexLock(&test->lock);
215
if (events & VIR_EVENT_HANDLE_WRITABLE) {
217
if ((ret = virNetSocketWrite(sock,
219
test->outgoingLength)) < 0) {
224
memmove(test->outgoing,
225
test->outgoing + ret,
226
test->outgoingLength - ret);
227
test->outgoingLength -= ret;
229
if ((test->outgoingCapacity - test->outgoingLength) > 1024)
230
VIR_SHRINK_N(test->outgoing, test->outgoingCapacity, 1024);
233
if (events & VIR_EVENT_HANDLE_READABLE) {
237
if ((test->incomingCapacity - test->incomingLength) < 1024) {
238
if (VIR_EXPAND_N(test->incoming, test->incomingCapacity, 1024) < 0) {
244
if ((ret = virNetSocketRead(sock,
245
test->incoming + test->incomingLength,
246
(test->incomingCapacity - test->incomingLength) - 1)) < 0) {
250
test->incomingLength += ret;
251
test->incoming[test->incomingLength] = '\0';
253
/* Look to see if we've got a complete line, and
254
* if so, handle that command
257
while ((t2 = strstr(t1, "\r\n"))) {
260
if (qemuMonitorTestProcessCommand(test, t1) < 0) {
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,
277
if (events & (VIR_EVENT_HANDLE_HANGUP |
278
VIR_EVENT_HANDLE_ERROR))
283
virNetSocketRemoveIOCallback(sock);
284
virNetSocketClose(sock);
285
virObjectUnref(test->client);
288
events = VIR_EVENT_HANDLE_READABLE;
290
if (test->outgoingLength)
291
events |= VIR_EVENT_HANDLE_WRITABLE;
293
virNetSocketUpdateIOCallback(sock, events);
295
virMutexUnlock(&test->lock);
299
static void qemuMonitorTestWorker(void *opaque)
301
qemuMonitorTestPtr test = opaque;
303
virMutexLock(&test->lock);
305
while (!test->quit) {
306
virMutexUnlock(&test->lock);
308
if (virEventRunDefaultImpl() < 0) {
313
virMutexLock(&test->lock);
316
test->running = false;
318
virMutexUnlock(&test->lock);
322
static void qemuMonitorTestItemFree(qemuMonitorTestItemPtr item)
327
VIR_FREE(item->command_name);
328
VIR_FREE(item->response);
334
void qemuMonitorTestFree(qemuMonitorTestPtr test)
341
virMutexLock(&test->lock);
345
virMutexUnlock(&test->lock);
348
virNetSocketRemoveIOCallback(test->client);
349
virNetSocketClose(test->client);
350
virObjectUnref(test->client);
353
virObjectUnref(test->server);
355
qemuMonitorUnlock(test->mon);
356
qemuMonitorClose(test->mon);
359
virObjectUnref(test->vm);
362
virThreadJoin(&test->thread);
364
for (i = 0 ; i < test->nitems ; i++)
365
qemuMonitorTestItemFree(test->items[i]);
366
VIR_FREE(test->items);
368
virMutexDestroy(&test->lock);
374
qemuMonitorTestAddItem(qemuMonitorTestPtr test,
375
const char *command_name,
376
const char *response)
378
qemuMonitorTestItemPtr item;
380
if (VIR_ALLOC(item) < 0)
383
if (!(item->command_name = strdup(command_name)) ||
384
!(item->response = strdup(response)))
387
virMutexLock(&test->lock);
388
if (VIR_EXPAND_N(test->items, test->nitems, 1) < 0) {
389
virMutexUnlock(&test->lock);
392
test->items[test->nitems - 1] = item;
394
virMutexUnlock(&test->lock);
400
qemuMonitorTestItemFree(item);
405
static void qemuMonitorTestEOFNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
406
virDomainObjPtr vm ATTRIBUTE_UNUSED)
410
static void qemuMonitorTestErrorNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
411
virDomainObjPtr vm ATTRIBUTE_UNUSED)
416
static qemuMonitorCallbacks qemuCallbacks = {
417
.eofNotify = qemuMonitorTestEOFNotify,
418
.errorNotify = qemuMonitorTestErrorNotify,
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"
424
qemuMonitorTestPtr qemuMonitorTestNew(bool json, virCapsPtr caps)
426
qemuMonitorTestPtr test = NULL;
427
virDomainChrSourceDef src;
429
char *tmpdir = NULL, *path = NULL;
430
char template[] = "/tmp/libvirt_XXXXXX";
432
tmpdir = mkdtemp(template);
433
if (tmpdir == NULL) {
434
virReportSystemError(errno, "%s",
435
"Failed to create temporary directory");
439
if (virAsprintf(&path, "%s/qemumonitorjsontest.sock", tmpdir) < 0) {
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;
449
if (VIR_ALLOC(test) < 0) {
454
if (virMutexInit(&test->lock) < 0) {
455
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
456
"Cannot initialize mutex");
462
if (!(test->vm = virDomainObjNew(caps)))
465
if (virNetSocketNewListenUNIX(path,
473
if (virNetSocketListen(test->server, 1) < 0)
476
if (!(test->mon = qemuMonitorOpen(test->vm,
481
qemuMonitorLock(test->mon);
483
if (virNetSocketAccept(test->server, &test->client) < 0)
488
if (qemuMonitorTestAddReponse(test, json ?
490
QEMU_TEXT_GREETING) < 0)
493
if (virNetSocketAddIOCallback(test->client,
494
VIR_EVENT_HANDLE_WRITABLE,
500
virMutexLock(&test->lock);
501
if (virThreadCreate(&test->thread,
503
qemuMonitorTestWorker,
505
virMutexUnlock(&test->lock);
508
test->running = true;
509
virMutexUnlock(&test->lock);
513
if (rmdir(tmpdir) < 0)
514
VIR_WARN("Failed to remove tempdir: %s", strerror(errno));
519
qemuMonitorTestFree(test);
523
qemuMonitorPtr qemuMonitorTestGetMonitor(qemuMonitorTestPtr test)