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(&reg, regex, REG_NOSUB | REG_EXTENDED) != 0) {
392         nm_bug("%s: regcomp failed", __func__);
393     }
394 
395     if (regexec(&reg, answer->data, 0, NULL, 0) != 0) {
396         rc = NM_ERR;
397     }
398 
399     regfree(&reg);
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