1 #include <nm_core.h>
2 #include <nm_dbus.h>
3 #include <nm_utils.h>
4 #include <nm_string.h>
5 #include <nm_window.h>
6 #include <nm_cfg_file.h>
7 #include <nm_usb_devices.h>
8 #include <nm_qmp_control.h>
9
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <mqueue.h>
13
14 #include <json.h>
15
16 enum {
17 NM_QMP_STATE_DONE = 0,
18 NM_QMP_STATE_MORE,
19 NM_QMP_STATE_NEXT,
20 NM_QMP_STATE_REPEAT,
21 NM_QMP_STATE_UNDEF
22 };
23
24 static const char NM_QMP_CMD_INIT[] = "{\"execute\":\"qmp_capabilities\"}";
25 static const char NM_QMP_CMD_VM_SHUT[] = "{\"execute\":\"system_powerdown\"}";
26 static const char NM_QMP_CMD_VM_QUIT[] = "{\"execute\":\"quit\"}";
27 static const char NM_QMP_CMD_VM_RESET[] = "{\"execute\":\"system_reset\"}";
28 static const char NM_QMP_CMD_VM_STOP[] = "{\"execute\":\"stop\"}";
29 static const char NM_QMP_CMD_VM_CONT[] = "{\"execute\":\"cont\"}";
30 static const char NM_QMP_CMD_JOBS[] = "{\"execute\":\"query-jobs\"}";
31
32 static const char NM_QMP_CMD_SAVEVM[] = \
33 "{\"execute\":\"snapshot-save\",\"arguments\":{\"job-id\":" \
34 "\"vmsave-%s-%s\",\"tag\":\"%s\",\"vmstate\":\"%s\",\"devices\":[%s]}}";
35
36 static const char NM_QMP_CMD_LOADVM[] = \
37 "{\"execute\":\"snapshot-load\",\"arguments\":{\"job-id\":" \
38 "\"vmload-%s-%s\",\"tag\":\"%s\",\"vmstate\":\"%s\",\"devices\":[%s]}}";
39
40 static const char NM_QMP_CMD_DELVM[] = \
41 "{\"execute\":\"snapshot-delete\",\"arguments\":{\"job-id\":" \
42 "\"vmdel-%s-%s\",\"tag\":\"%s\",\"devices\":[%s]}}";
43
44 static const char NM_QMP_CMD_USB_ADD[] = \
45 "{\"execute\":\"device_add\",\"arguments\":{\"driver\":\"usb-host\"," \
46 "\"hostbus\":\"%u\",\"hostaddr\":\"%u\",\"id\":\"usb-%s-%s-%s\"}}";
47
48 static const char NM_QMP_CMD_USB_DEL[] = \
49 "{\"execute\":\"device_del\",\"arguments\":{\"id\":\"usb-%s-%s-%s\"}}";
50
51 /* Get peripheral qmp command example
52
53 input:
54
55 {"execute": "qom-list", "arguments": { "path": "/machine/peripheral" }}
56
57 output:
58
59 {"return": [{"name": "type", "type": "string"}, {"name": "dev1", "type": "child<usb-host>"},
60 {"name": "dev2", "type": "child<usb-host>"}]}
61 */
62
63 enum {NM_QMP_READLEN = 1024};
64
65 typedef struct {
66 int sd;
67 struct sockaddr_un sock;
68 } nm_qmp_handle_t;
69
70 #define NM_INIT_QMP (nm_qmp_handle_t) { .sd = -1 }
71
72 static int nm_qmp_vm_exec(const nm_str_t *name, const char *cmd,
73 struct timeval *tv);
74 static int nm_qmp_init_cmd(nm_qmp_handle_t *h);
75 static void nm_qmp_sock_path(const nm_str_t *name, nm_str_t *path);
76 static int nm_qmp_talk(int sd, const char *cmd,
77 size_t len, struct timeval *tv);
78 static void nm_qmp_talk_async(int sd, const char *cmd,
79 size_t len, const char *jobid);
80 static int nm_qmp_send(const nm_str_t *cmd);
81 static int nm_qmp_check_answer(const nm_str_t *answer);
82 static int nm_qmp_parse(const char *jobid, const nm_str_t *answer);
83 static int nm_qmp_check_job(const char *jobid, const nm_str_t *answer);
84
nm_qmp_vm_shut(const nm_str_t * name)85 void nm_qmp_vm_shut(const nm_str_t *name)
86 {
87 struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 }; /* 0.1s */
88
89 nm_qmp_vm_exec(name, NM_QMP_CMD_VM_SHUT, &tv);
90 }
91
nm_qmp_vm_stop(const nm_str_t * name)92 void nm_qmp_vm_stop(const nm_str_t *name)
93 {
94 struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 }; /* 0.1s */
95
96 nm_qmp_vm_exec(name, NM_QMP_CMD_VM_QUIT, &tv);
97 }
98
nm_qmp_vm_reset(const nm_str_t * name)99 void nm_qmp_vm_reset(const nm_str_t *name)
100 {
101 struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 }; /* 0.1s */
102
103 nm_qmp_vm_exec(name, NM_QMP_CMD_VM_RESET, &tv);
104 }
105
nm_qmp_vm_pause(const nm_str_t * name)106 void nm_qmp_vm_pause(const nm_str_t *name)
107 {
108 struct timeval tv = { .tv_sec = 0, .tv_usec = 1000000 }; /* 1s */
109
110 nm_qmp_vm_exec(name, NM_QMP_CMD_VM_STOP, &tv);
111 }
112
nm_qmp_vm_resume(const nm_str_t * name)113 void nm_qmp_vm_resume(const nm_str_t *name)
114 {
115 struct timeval tv = { .tv_sec = 0, .tv_usec = 1000000 }; /* 1s */
116
117 nm_qmp_vm_exec(name, NM_QMP_CMD_VM_CONT, &tv);
118 }
119
nm_qmp_savevm(const nm_str_t * name,const nm_str_t * snap)120 int nm_qmp_savevm(const nm_str_t *name, const nm_str_t *snap)
121 {
122 nm_vect_t drives = NM_INIT_VECT;
123 nm_str_t query = NM_INIT_STR;
124 nm_str_t devs = NM_INIT_STR;
125 nm_str_t uid = NM_INIT_STR;
126 nm_str_t cmd = NM_INIT_STR;
127 size_t drives_count;
128 int rc;
129
130 nm_str_format(&query, NM_VM_GET_DRIVES_SQL, name->data);
131 nm_db_select(query.data, &drives);
132 drives_count = drives.n_memb / NM_DRV_IDX_COUNT;
133
134 for (size_t n = 0; n < drives_count; n++) {
135 nm_str_append_format(&devs, "%s\"hd%zu\"", (!n) ? "" : ", ", n);
136 }
137
138 nm_gen_uid(&uid);
139
140 nm_str_format(&cmd, NM_QMP_CMD_SAVEVM, name->data, uid.data, snap->data,
141 "hd0", devs.data);
142 rc = nm_qmp_send(&cmd);
143
144 nm_vect_free(&drives, nm_str_vect_free_cb);
145 nm_str_free(&devs);
146 nm_str_free(&cmd);
147 nm_str_free(&query);
148 nm_str_free(&uid);
149
150 return rc;
151 }
152
nm_qmp_loadvm(const nm_str_t * name,const nm_str_t * snap)153 int nm_qmp_loadvm(const nm_str_t *name, const nm_str_t *snap)
154 {
155 nm_vect_t drives = NM_INIT_VECT;
156 nm_str_t query = NM_INIT_STR;
157 nm_str_t devs = NM_INIT_STR;
158 nm_str_t uid = NM_INIT_STR;
159 nm_str_t cmd = NM_INIT_STR;
160 size_t drives_count;
161 int rc;
162
163 nm_str_format(&query, NM_VM_GET_DRIVES_SQL, name->data);
164 nm_db_select(query.data, &drives);
165 drives_count = drives.n_memb / NM_DRV_IDX_COUNT;
166
167 for (size_t n = 0; n < drives_count; n++) {
168 nm_str_append_format(&devs, "%s\"hd%zu\"", (!n) ? "" : ", ", n);
169 }
170
171 nm_gen_uid(&uid);
172
173 nm_str_format(&cmd, NM_QMP_CMD_LOADVM, name->data, uid.data, snap->data,
174 "hd0", devs.data);
175 rc = nm_qmp_send(&cmd);
176
177 nm_vect_free(&drives, nm_str_vect_free_cb);
178 nm_str_free(&devs);
179 nm_str_free(&cmd);
180 nm_str_free(&query);
181 nm_str_free(&uid);
182
183 return rc;
184 }
185
nm_qmp_delvm(const nm_str_t * name,const nm_str_t * snap)186 int nm_qmp_delvm(const nm_str_t *name, const nm_str_t *snap)
187 {
188 nm_vect_t drives = NM_INIT_VECT;
189 nm_str_t query = NM_INIT_STR;
190 nm_str_t devs = NM_INIT_STR;
191 nm_str_t uid = NM_INIT_STR;
192 nm_str_t cmd = NM_INIT_STR;
193 size_t drives_count;
194 int rc;
195
196 nm_str_format(&query, NM_VM_GET_DRIVES_SQL, name->data);
197 nm_db_select(query.data, &drives);
198 drives_count = drives.n_memb / NM_DRV_IDX_COUNT;
199
200 for (size_t n = 0; n < drives_count; n++) {
201 nm_str_append_format(&devs, "%s\"hd%zu\"", (!n) ? "" : ", ", n);
202 }
203
204 nm_gen_uid(&uid);
205
206 nm_str_format(&cmd, NM_QMP_CMD_DELVM, name->data, uid.data,
207 snap->data, devs.data);
208 rc = nm_qmp_send(&cmd);
209
210 nm_vect_free(&drives, nm_str_vect_free_cb);
211 nm_str_free(&devs);
212 nm_str_free(&cmd);
213 nm_str_free(&query);
214 nm_str_free(&uid);
215
216 return rc;
217 }
218
nm_qmp_usb_attach(const nm_str_t * name,const nm_usb_data_t * usb)219 int nm_qmp_usb_attach(const nm_str_t *name, const nm_usb_data_t *usb)
220 {
221 nm_str_t qmp_query = NM_INIT_STR;
222 int rc;
223
224 struct timeval tv = { .tv_sec = 0, .tv_usec = 5000000 }; /* 5s */
225
226 nm_str_format(&qmp_query, NM_QMP_CMD_USB_ADD,
227 usb->dev->bus_num, usb->dev->dev_addr,
228 usb->dev->vendor_id.data, usb->dev->product_id.data,
229 (usb->serial.len) ? usb->serial.data : "NULL");
230
231 nm_debug("exec qmp: %s\n", qmp_query.data);
232 rc = nm_qmp_vm_exec(name, qmp_query.data, &tv);
233
234 nm_str_free(&qmp_query);
235
236 return rc;
237 }
238
nm_qmp_usb_detach(const nm_str_t * name,const nm_usb_data_t * usb)239 int nm_qmp_usb_detach(const nm_str_t *name, const nm_usb_data_t *usb)
240 {
241 nm_str_t qmp_query = NM_INIT_STR;
242 int rc;
243
244 struct timeval tv = { .tv_sec = 0, .tv_usec = 1000000 }; /* 1s */
245
246 nm_str_format(&qmp_query, NM_QMP_CMD_USB_DEL,
247 usb->dev->vendor_id.data,
248 usb->dev->product_id.data,
249 (usb->serial.len) ? usb->serial.data : "NULL");
250
251 nm_debug("exec qmp: %s\n", qmp_query.data);
252 rc = nm_qmp_vm_exec(name, qmp_query.data, &tv);
253
254 nm_str_free(&qmp_query);
255
256 return rc;
257 }
258
nm_qmp_test_socket(const nm_str_t * name)259 int nm_qmp_test_socket(const nm_str_t *name)
260 {
261 int rc = NM_ERR;
262 nm_str_t sock_path = NM_INIT_STR;
263 nm_qmp_handle_t qmp = NM_INIT_QMP;
264
265 nm_qmp_sock_path(name, &sock_path);
266
267 qmp.sock.sun_family = AF_UNIX;
268 nm_strlcpy(qmp.sock.sun_path, sock_path.data, sizeof(qmp.sock.sun_path));
269
270 if ((qmp.sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
271 goto out;
272
273 if (fcntl(qmp.sd, F_SETFL, O_NONBLOCK) == -1) {
274 close(qmp.sd);
275 goto out;
276 }
277
278 if (connect(qmp.sd, (struct sockaddr *) &qmp.sock, sizeof(qmp.sock)) != 0) {
279 if (errno == EAGAIN) {
280 rc = NM_OK;
281 }
282 } else {
283 rc = NM_OK;
284 }
285
286 close(qmp.sd);
287 out:
288 nm_str_free(&sock_path);
289
290 return rc;
291 }
292
nm_qmp_send(const nm_str_t * cmd)293 static int nm_qmp_send(const nm_str_t *cmd)
294 {
295 mqd_t mq;
296
297 if ((mq = mq_open(NM_MQ_PATH, O_WRONLY | O_CREAT | O_NONBLOCK,
298 0600, NULL)) == (mqd_t) -1) {
299 return NM_ERR;
300 }
301
302 if (mq_send(mq, cmd->data, cmd->len, 0) == EAGAIN) {
303 return NM_ERR;
304 }
305
306 mq_close(mq);
307
308 return NM_OK;
309 }
310
nm_qmp_vm_exec(const nm_str_t * name,const char * cmd,struct timeval * tv)311 static int nm_qmp_vm_exec(const nm_str_t *name, const char *cmd,
312 struct timeval *tv)
313 {
314 nm_str_t sock_path = NM_INIT_STR;
315 nm_qmp_handle_t qmp = NM_INIT_QMP;
316 int rc = NM_ERR;
317
318 nm_qmp_sock_path(name, &sock_path);
319
320 qmp.sock.sun_family = AF_UNIX;
321 nm_strlcpy(qmp.sock.sun_path, sock_path.data, sizeof(qmp.sock.sun_path));
322
323 if (nm_qmp_init_cmd(&qmp) == NM_ERR)
324 goto out;
325
326 rc = nm_qmp_talk(qmp.sd, cmd, strlen(cmd), tv);
327 close(qmp.sd);
328
329 out:
330 nm_str_free(&sock_path);
331 return rc;
332 }
333
nm_qmp_vm_exec_async(const nm_str_t * name,const char * cmd,const char * jobid)334 void nm_qmp_vm_exec_async(const nm_str_t *name, const char *cmd,
335 const char *jobid)
336 {
337 nm_str_t sock_path = NM_INIT_STR;
338 nm_qmp_handle_t qmp = NM_INIT_QMP;
339
340 nm_qmp_sock_path(name, &sock_path);
341
342 qmp.sock.sun_family = AF_UNIX;
343 nm_strlcpy(qmp.sock.sun_path, sock_path.data, sizeof(qmp.sock.sun_path));
344
345 if (nm_qmp_init_cmd(&qmp) == NM_ERR)
346 goto out;
347
348 nm_qmp_talk_async(qmp.sd, cmd, strlen(cmd), jobid);
349 close(qmp.sd);
350
351 out:
352 nm_str_free(&sock_path);
353 }
354
nm_qmp_init_cmd(nm_qmp_handle_t * h)355 static int nm_qmp_init_cmd(nm_qmp_handle_t *h)
356 {
357 socklen_t len = sizeof(h->sock);
358 struct timeval tv;
359
360 tv.tv_sec = 0;
361 tv.tv_usec = 100000; /* 0.1 s */
362
363 if ((h->sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
364 nm_warn(_(NM_MSG_Q_CR_ERR));
365 return NM_ERR;
366 }
367
368 if (fcntl(h->sd, F_SETFL, O_NONBLOCK) == -1) {
369 close(h->sd);
370 nm_warn(_(NM_MSG_Q_FL_ERR));
371 return NM_ERR;
372 }
373
374 if (connect(h->sd, (struct sockaddr *) &h->sock, len) == -1) {
375 close(h->sd);
376 nm_warn(_(NM_MSG_Q_CN_ERR));
377 return NM_ERR;
378 }
379
380 return nm_qmp_talk(h->sd, NM_QMP_CMD_INIT, strlen(NM_QMP_CMD_INIT), &tv);
381 }
382
nm_qmp_check_answer(const nm_str_t * answer)383 static int nm_qmp_check_answer(const nm_str_t *answer)
384 {
385 /* {"return": {}} from answer means OK
386 * TODO: use JSON parser instead, e.g: json-c */
387 const char *regex = ".*\\{\"return\":[[:space:]]\\{\\}\\}.*";
388 regex_t reg;
389 int rc = NM_OK;
390
391 if (regcomp(®, regex, REG_NOSUB | REG_EXTENDED) != 0) {
392 nm_bug("%s: regcomp failed", __func__);
393 }
394
395 if (regexec(®, answer->data, 0, NULL, 0) != 0) {
396 rc = NM_ERR;
397 }
398
399 regfree(®);
400
401 return rc;
402 }
403
nm_qmp_parse(const char * jobid,const nm_str_t * answer)404 static int nm_qmp_parse(const char *jobid, const nm_str_t *answer)
405 {
406 int state = NM_QMP_STATE_UNDEF;
407 nm_vect_t json = NM_INIT_VECT;
408 char *saveptr;
409 char *token;
410
411 saveptr = answer->data;
412
413 while ((token = strtok_r(saveptr, "\n", &saveptr))) {
414 nm_str_t js = NM_INIT_STR;
415 nm_str_format(&js, "%s", token);
416 nm_vect_insert(&json, &js, sizeof(nm_str_t), nm_str_vect_ins_cb);
417 nm_str_free(&js);
418 }
419
420 if (json.n_memb == 1) {
421 nm_debug("%s: single json\n", __func__);
422 state = nm_qmp_check_job(jobid, nm_vect_at(&json, 0));
423 goto out;
424 }
425
426 nm_debug("%s: multiple json: %zu\n", __func__, json.n_memb);
427 for (size_t n = 0; n < json.n_memb; n++) {
428 if ((state = nm_qmp_check_job(jobid, nm_vect_at(&json, n)))
429 == NM_QMP_STATE_DONE) {
430 break;
431 }
432 }
433
434 out:
435 nm_vect_free(&json, nm_str_vect_free_cb);
436
437 return state;
438 }
439
nm_qmp_check_job(const char * jobid,const nm_str_t * answer)440 static int nm_qmp_check_job(const char *jobid, const nm_str_t *answer)
441 {
442 struct json_object *parsed, *ret, *job, *id, *err, *status;
443 int state = NM_QMP_STATE_DONE;
444 nm_str_t body = NM_INIT_STR;
445 int jobs = 0;
446
447 nm_debug("%s: checking job: %s\n", __func__, jobid);
448 nm_debug("%s: answer: %s\n", __func__, answer->data);
449
450 parsed = json_tokener_parse(answer->data);
451 if (!parsed) {
452 nm_debug("%s: [more] need more data\n", __func__);
453 state = NM_QMP_STATE_MORE;
454 goto out;
455 }
456
457 json_object_object_get_ex(parsed, "return", &ret);
458 if (ret == NULL) {
459 nm_debug("%s: [next] need more data\n", __func__);
460 state = NM_QMP_STATE_NEXT;
461 goto out;
462 }
463
464 /* skip {"return": {}} */
465 if (json_object_get_type(ret) != json_type_array) {
466 nm_debug("%s: [ret next] need more data\n", __func__);
467 state = NM_QMP_STATE_NEXT;
468 goto out;
469 }
470
471 jobs = json_object_array_length(ret);
472 if (!jobs) {
473 nm_debug("%s: [ret next] need more data\n", __func__);
474 state = NM_QMP_STATE_REPEAT;
475 goto out;
476 }
477
478 nm_debug("%s: got %d jobs\n", __func__, jobs);
479 for (int i = 0; i < jobs; i++) {
480 const char *id_str;
481
482 job = json_object_array_get_idx(ret, i);
483 json_object_object_get_ex(job, "id", &id);
484 id_str = json_object_get_string(id);
485
486 if (nm_str_cmp_tt(id_str, jobid) == NM_OK) {
487 const char *status_str;
488 nm_debug("%s: job found: %s, checking status\n", __func__, id_str);
489
490 json_object_object_get_ex(job, "status", &status);
491 status_str = json_object_get_string(status);
492 if (nm_str_cmp_tt(status_str, "concluded") != NM_OK) {
493 nm_debug("%s: job %s is not finished yet\n", __func__, id_str);
494 state = NM_QMP_STATE_REPEAT;
495 break;
496 }
497
498 json_object_object_get_ex(job, "error", &err);
499 if (err) { /* job executed with errors */
500 const char *err_str;
501 err_str = json_object_get_string(err);
502 nm_debug("%s: job %s executed with error: %s\n",
503 __func__, id_str, err_str);
504 #if defined (NM_WITH_DBUS)
505 nm_str_format(&body, "%s - %s", id_str, err_str);
506 nm_dbus_send_notify("Job finished with error:", body.data);
507 #endif
508 break;
509 }
510
511 /*job finished successfully */
512 nm_debug("%s: job %s executed successfully\n", __func__, id_str);
513 #if defined (NM_WITH_DBUS)
514 nm_str_format(&body, "%s", id_str);
515 nm_dbus_send_notify("Job finished successfully:", body.data);
516 #endif
517 break;
518 }
519 }
520 out:
521 json_object_put(parsed);
522 nm_str_free(&body);
523
524 return state;
525 }
526
nm_qmp_talk(int sd,const char * cmd,size_t len,struct timeval * tv)527 static int nm_qmp_talk(int sd, const char *cmd,
528 size_t len, struct timeval *tv)
529 {
530 nm_str_t answer = NM_INIT_STR;
531 char buf[NM_QMP_READLEN];
532 ssize_t nread;
533 fd_set readset;
534 int ret, read_done = 0;
535 int rc = NM_OK;
536
537 FD_ZERO(&readset);
538 FD_SET(sd, &readset);
539
540 if (write(sd, cmd, len) == -1) {
541 close(sd);
542 nm_warn(_(NM_MSG_Q_SE_ERR));
543 return NM_ERR;
544 }
545
546 while (!read_done) {
547 ret = select(sd + 1, &readset, NULL, NULL, tv);
548 if (ret == -1) {
549 nm_bug("%s: select error: %s", __func__, strerror(errno));
550 } else if (ret && FD_ISSET(sd, &readset)) { /* data is available */
551 memset(buf, 0, NM_QMP_READLEN);
552 nread = read(sd, buf, NM_QMP_READLEN);
553 if (nread > 1) {
554 buf[nread - 2] = '\0';
555 nm_str_add_text(&answer, buf);
556 /* check for command successfully executed here
557 * and return if it done */
558 if ((rc = nm_qmp_check_answer(&answer)) == NM_OK)
559 goto out;
560 } else if (nread == 0) { /* socket closed */
561 read_done = 1;
562 }
563 } else { /* timeout, nothing happens */
564 read_done = 1;
565 }
566 }
567
568 if (answer.len == 0) {
569 nm_warn(_(NM_MSG_Q_NO_ANS));
570 rc = NM_ERR;
571 goto err;
572 }
573
574 out:
575 nm_debug("QMP: %s\n", answer.data);
576 if (rc != NM_OK)
577 nm_warn(_(NM_MSG_Q_EXEC_E));
578 err:
579 nm_str_free(&answer);
580
581 return rc;
582 }
583
nm_qmp_talk_async(int sd,const char * cmd,size_t len,const char * jobid)584 static void nm_qmp_talk_async(int sd, const char *cmd,
585 size_t len, const char *jobid)
586 {
587 char buf[NM_QMP_READLEN + 1] = {0};
588 nm_str_t answer = NM_INIT_STR;
589 int ret, read_done = 0;
590 struct timeval tv;
591 int state = NM_QMP_STATE_UNDEF;
592 fd_set readset;
593 ssize_t nread;
594
595 tv.tv_sec = 300;
596 tv.tv_usec = 0;
597
598 FD_ZERO(&readset);
599 FD_SET(sd, &readset);
600
601 if (write(sd, cmd, len) == -1) {
602 close(sd);
603 //nm_warn(_(NM_MSG_Q_SE_ERR));
604 return;
605 }
606
607 if (write(sd, NM_QMP_CMD_JOBS, sizeof(NM_QMP_CMD_JOBS) - 1) == -1) {
608 close(sd);
609 //nm_warn(_(NM_MSG_Q_SE_ERR));
610 return;
611 }
612
613 while (!read_done) {
614 /* query jobs */
615 if (state == NM_QMP_STATE_REPEAT) {
616 if (write(sd, NM_QMP_CMD_JOBS, sizeof(NM_QMP_CMD_JOBS) - 1) == -1) {
617 close(sd);
618 //nm_warn(_(NM_MSG_Q_SE_ERR));
619 return;
620 }
621 state = NM_QMP_STATE_UNDEF;
622 }
623
624 ret = select(sd + 1, &readset, NULL, NULL, &tv);
625 if (ret == -1) {
626 nm_bug("%s: select error: %s", __func__, strerror(errno));
627 } else if (ret && FD_ISSET(sd, &readset)) { /* data is available */
628 memset(buf, 0, NM_QMP_READLEN);
629 nread = read(sd, buf, NM_QMP_READLEN);
630 if (nread > 1) {
631 if (buf[nread - 2] == '\r') {
632 buf[nread - 2] = '\0';
633 }
634 if (state == NM_QMP_STATE_MORE) {
635 nm_str_add_text(&answer, buf);
636 } else {
637 nm_str_format(&answer, "%s", buf);
638 }
639 /* check for job successfully finished
640 * and return if it done */
641 if ((state = nm_qmp_parse(jobid, &answer)) == NM_QMP_STATE_DONE)
642 goto out;
643 } else if (nread == 0) { /* socket closed */
644 read_done = 1;
645 }
646 } else { /* timeout, nothing happens */
647 read_done = 1;
648 }
649 }
650
651 if (answer.len == 0) {
652 //nm_warn(_(NM_MSG_Q_NO_ANS));
653 }
654
655 out:
656 nm_str_free(&answer);
657 }
658
nm_qmp_sock_path(const nm_str_t * name,nm_str_t * path)659 static void nm_qmp_sock_path(const nm_str_t *name, nm_str_t *path)
660 {
661 nm_str_format(path, "%s/%s/qmp.sock",
662 nm_cfg_get()->vm_dir.data, name->data);
663 }
664
665 /* vim:set ts=4 sw=4: */
666