1 /*
2 * Copyright 2009-2017 Citrix Ltd and other contributors
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; version 2.1 only. with the special
7 * exception on linking described in file LICENSE.
8 *
9 * This program 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
12 * GNU Lesser General Public License for more details.
13 */
14
15 #include "libxl_osdeps.h"
16
17 #include "libxl_internal.h"
18
19 #define PAGE_TO_MEMKB(pages) ((pages) * 4)
20
libxl__domain_rename(libxl__gc * gc,uint32_t domid,const char * old_name,const char * new_name,xs_transaction_t trans)21 int libxl__domain_rename(libxl__gc *gc, uint32_t domid,
22 const char *old_name, const char *new_name,
23 xs_transaction_t trans)
24 {
25 libxl_ctx *ctx = libxl__gc_owner(gc);
26 char *dom_path = 0;
27 const char *name_path;
28 char *got_old_name;
29 unsigned int got_old_len;
30 xs_transaction_t our_trans = 0;
31 uint32_t stub_dm_domid;
32 const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL;
33 int rc;
34 libxl_dominfo info;
35 char *uuid;
36 const char *vm_name_path;
37
38 libxl_dominfo_init(&info);
39
40 dom_path = libxl__xs_get_dompath(gc, domid);
41 if (!dom_path) goto x_nomem;
42
43 name_path= GCSPRINTF("%s/name", dom_path);
44 if (!name_path) goto x_nomem;
45
46 stub_dm_domid = libxl_get_stubdom_id(CTX, domid);
47 if (stub_dm_domid) {
48 stub_dm_old_name = libxl__stub_dm_name(gc, old_name);
49 stub_dm_new_name = libxl__stub_dm_name(gc, new_name);
50 }
51
52 retry_transaction:
53 if (!trans) {
54 trans = our_trans = xs_transaction_start(ctx->xsh);
55 if (!our_trans) {
56 LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name");
57 goto x_fail;
58 }
59 }
60
61 if (!new_name) {
62 LOGD(ERROR, domid, "New domain name not specified");
63 rc = ERROR_INVAL;
64 goto x_rc;
65 }
66
67 if (new_name[0]) {
68 /* nonempty names must be unique */
69 uint32_t domid_e;
70 rc = libxl_name_to_domid(ctx, new_name, &domid_e);
71 if (rc == ERROR_INVAL) {
72 /* no such domain, good */
73 } else if (rc != 0) {
74 LOGD(ERROR, domid, "Unexpected error checking for existing domain");
75 goto x_rc;
76 } else if (domid_e == domid) {
77 /* domain already has this name, ok (but we do still
78 * need the rest of the code as we may need to check
79 * old_name, for example). */
80 } else {
81 LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name);
82 rc = ERROR_INVAL;
83 goto x_rc;
84 }
85 }
86
87 if (old_name) {
88 got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len);
89 if (!got_old_name) {
90 LOGEVD(ERROR, errno, domid,
91 "Check old name for domain allegedly named `%s'",
92 old_name);
93 goto x_fail;
94 }
95 if (strcmp(old_name, got_old_name)) {
96 LOGD(ERROR, domid,
97 "Allegedly named `%s' is actually named `%s' - racing ?",
98 old_name,
99 got_old_name);
100 free(got_old_name);
101 goto x_fail;
102 }
103 free(got_old_name);
104 }
105 if (!xs_write(ctx->xsh, trans, name_path,
106 new_name, strlen(new_name))) {
107 LOGD(ERROR, domid,
108 "Failed to write new name `%s'"
109 " for domain previously named `%s'",
110 new_name,
111 old_name);
112 goto x_fail;
113 }
114
115 /* update /vm/<uuid>/name */
116 rc = libxl_domain_info(ctx, &info, domid);
117 if (rc)
118 goto x_rc;
119
120 uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid));
121 vm_name_path = GCSPRINTF("/vm/%s/name", uuid);
122 if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name))
123 goto x_fail;
124
125 if (stub_dm_domid) {
126 rc = libxl__domain_rename(gc, stub_dm_domid,
127 stub_dm_old_name,
128 stub_dm_new_name,
129 trans);
130 if (rc) {
131 LOGED(ERROR, domid, "Unable to rename stub-domain");
132 goto x_rc;
133 }
134 }
135
136 if (our_trans) {
137 if (!xs_transaction_end(ctx->xsh, our_trans, 0)) {
138 trans = our_trans = 0;
139 if (errno != EAGAIN) {
140 LOGD(ERROR, domid,
141 "Failed to commit new name `%s'"
142 " for domain previously named `%s'",
143 new_name,
144 old_name);
145 goto x_fail;
146 }
147 LOGD(DEBUG, domid,
148 "Need to retry rename transaction"
149 " for domain (name_path=\"%s\", new_name=\"%s\")",
150 name_path,
151 new_name);
152 goto retry_transaction;
153 }
154 our_trans = 0;
155 }
156
157 rc = 0;
158 x_rc:
159 if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1);
160 libxl_dominfo_dispose(&info);
161 return rc;
162
163 x_fail: rc = ERROR_FAIL; goto x_rc;
164 x_nomem: rc = ERROR_NOMEM; goto x_rc;
165 }
166
libxl_domain_rename(libxl_ctx * ctx,uint32_t domid,const char * old_name,const char * new_name)167 int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
168 const char *old_name, const char *new_name)
169 {
170 GC_INIT(ctx);
171 int rc;
172 rc = libxl__domain_rename(gc, domid, old_name, new_name, XBT_NULL);
173 GC_FREE;
174 return rc;
175 }
176
177 static void domain_resume_done(libxl__egc *egc,
178 libxl__dm_resume_state *,
179 int rc);
180
libxl_domain_resume(libxl_ctx * ctx,uint32_t domid,int suspend_cancel,const libxl_asyncop_how * ao_how)181 int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
182 const libxl_asyncop_how *ao_how)
183 {
184 AO_CREATE(ctx, domid, ao_how);
185 libxl__dm_resume_state *dmrs;
186
187 GCNEW(dmrs);
188 dmrs->ao = ao;
189 dmrs->domid = domid;
190 dmrs->callback = domain_resume_done;
191 libxl__domain_resume(egc, dmrs, suspend_cancel);
192 return AO_INPROGRESS;
193 }
194
domain_resume_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)195 static void domain_resume_done(libxl__egc *egc,
196 libxl__dm_resume_state *dmrs,
197 int rc)
198 {
199 STATE_AO_GC(dmrs->ao);
200 libxl__ao_complete(egc, ao, rc);
201 }
202
203 /*
204 * Preserves a domain but rewrites xenstore etc to make it unique so
205 * that the domain can be restarted.
206 *
207 * Does not modify info so that it may be reused.
208 */
libxl_domain_preserve(libxl_ctx * ctx,uint32_t domid,libxl_domain_create_info * info,const char * name_suffix,libxl_uuid new_uuid)209 int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
210 libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
211 {
212 GC_INIT(ctx);
213 struct xs_permissions roperm[2];
214 xs_transaction_t t;
215 char *preserved_name;
216 char *uuid_string;
217 char *vm_path;
218 char *dom_path;
219
220 int rc;
221
222 preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
223 if (!preserved_name) {
224 GC_FREE;
225 return ERROR_NOMEM;
226 }
227
228 uuid_string = libxl__uuid2string(gc, new_uuid);
229 if (!uuid_string) {
230 GC_FREE;
231 return ERROR_NOMEM;
232 }
233
234 dom_path = libxl__xs_get_dompath(gc, domid);
235 if (!dom_path) {
236 GC_FREE;
237 return ERROR_FAIL;
238 }
239
240 vm_path = GCSPRINTF("/vm/%s", uuid_string);
241 if (!vm_path) {
242 GC_FREE;
243 return ERROR_FAIL;
244 }
245
246 roperm[0].id = 0;
247 roperm[0].perms = XS_PERM_NONE;
248 roperm[1].id = domid;
249 roperm[1].perms = XS_PERM_READ;
250
251 retry_transaction:
252 t = xs_transaction_start(ctx->xsh);
253
254 xs_rm(ctx->xsh, t, vm_path);
255 xs_mkdir(ctx->xsh, t, vm_path);
256 xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
257
258 xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
259 rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
260 if (rc) {
261 GC_FREE;
262 return rc;
263 }
264
265 xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
266
267 if (!xs_transaction_end(ctx->xsh, t, 0))
268 if (errno == EAGAIN)
269 goto retry_transaction;
270
271 GC_FREE;
272 return 0;
273 }
274
libxl__xcinfo2xlinfo(libxl_ctx * ctx,const xc_domaininfo_t * xcinfo,libxl_dominfo * xlinfo)275 void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
276 const xc_domaininfo_t *xcinfo,
277 libxl_dominfo *xlinfo)
278 {
279 size_t size;
280
281 memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
282 xlinfo->domid = xcinfo->domain;
283 xlinfo->ssidref = xcinfo->ssidref;
284 if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
285 &xlinfo->ssid_label, &size) < 0)
286 xlinfo->ssid_label = NULL;
287
288 xlinfo->dying = !!(xcinfo->flags&XEN_DOMINF_dying);
289 xlinfo->shutdown = !!(xcinfo->flags&XEN_DOMINF_shutdown);
290 xlinfo->paused = !!(xcinfo->flags&XEN_DOMINF_paused);
291 xlinfo->blocked = !!(xcinfo->flags&XEN_DOMINF_blocked);
292 xlinfo->running = !!(xcinfo->flags&XEN_DOMINF_running);
293 xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
294
295 if (xlinfo->shutdown)
296 xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
297 else
298 xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
299
300 xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
301 xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
302 xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
303 xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
304 xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
305 xlinfo->cpu_time = xcinfo->cpu_time;
306 xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
307 xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
308 xlinfo->cpupool = xcinfo->cpupool;
309 xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
310 LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
311 }
312
libxl_list_domain(libxl_ctx * ctx,int * nb_domain_out)313 libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
314 {
315 libxl_dominfo *ptr = NULL;
316 int i, ret;
317 xc_domaininfo_t *info;
318 int size = 0;
319 uint32_t domid = 0;
320 GC_INIT(ctx);
321
322 GCNEW_ARRAY(info, 1024);
323
324 while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
325 ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
326 for (i = 0; i < ret; i++) {
327 libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
328 }
329 domid = info[ret - 1].domain + 1;
330 size += ret;
331 }
332
333 if (ret < 0) {
334 LOGE(ERROR, "getting domain info list");
335 free(ptr);
336 GC_FREE;
337 return NULL;
338 }
339
340 *nb_domain_out = size;
341 GC_FREE;
342 return ptr;
343 }
344
libxl_domain_info(libxl_ctx * ctx,libxl_dominfo * info_r,uint32_t domid)345 int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
346 uint32_t domid) {
347 xc_domaininfo_t xcinfo;
348 int ret;
349 GC_INIT(ctx);
350
351 ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
352 if (ret<0) {
353 LOGED(ERROR, domid, "Getting domain info list");
354 GC_FREE;
355 return ERROR_FAIL;
356 }
357 if (ret==0 || xcinfo.domain != domid) {
358 GC_FREE;
359 return ERROR_DOMAIN_NOTFOUND;
360 }
361
362 if (info_r)
363 libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
364 GC_FREE;
365 return 0;
366 }
367
368 /* this API call only list VM running on this host. A VM can
369 * be an aggregate of multiple domains. */
libxl_list_vm(libxl_ctx * ctx,int * nb_vm_out)370 libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
371 {
372 GC_INIT(ctx);
373 libxl_dominfo *info;
374 libxl_vminfo *ptr = NULL;
375 int idx, i, n_doms;
376
377 info = libxl_list_domain(ctx, &n_doms);
378 if (!info)
379 goto out;
380
381 /*
382 * Always make sure to allocate at least one element; if we don't and we
383 * request zero, libxl__calloc (might) think its internal call to calloc
384 * has failed (if it returns null), if so it would kill our process.
385 */
386 ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
387
388 for (idx = i = 0; i < n_doms; i++) {
389 if (libxl_is_stubdom(ctx, info[i].domid, NULL))
390 continue;
391 ptr[idx].uuid = info[i].uuid;
392 ptr[idx].domid = info[i].domid;
393
394 idx++;
395 }
396 *nb_vm_out = idx;
397 libxl_dominfo_list_free(info, n_doms);
398
399 out:
400 GC_FREE;
401 return ptr;
402 }
403
404 static void remus_failover_cb(libxl__egc *egc,
405 libxl__domain_save_state *dss, int rc);
406
libxl_domain_remus_start(libxl_ctx * ctx,libxl_domain_remus_info * info,uint32_t domid,int send_fd,int recv_fd,const libxl_asyncop_how * ao_how)407 int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
408 uint32_t domid, int send_fd, int recv_fd,
409 const libxl_asyncop_how *ao_how)
410 {
411 AO_CREATE(ctx, domid, ao_how);
412 libxl__domain_save_state *dss;
413 int rc;
414
415 libxl_domain_type type = libxl__domain_type(gc, domid);
416 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
417 rc = ERROR_FAIL;
418 goto out;
419 }
420
421 /* The caller must set this defbool */
422 if (libxl_defbool_is_default(info->colo)) {
423 LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
424 rc = ERROR_FAIL;
425 goto out;
426 }
427
428 libxl_defbool_setdefault(&info->allow_unsafe, false);
429 libxl_defbool_setdefault(&info->blackhole, false);
430 libxl_defbool_setdefault(&info->compression,
431 !libxl_defbool_val(info->colo));
432 libxl_defbool_setdefault(&info->netbuf, true);
433 libxl_defbool_setdefault(&info->diskbuf, true);
434
435 if (libxl_defbool_val(info->colo) &&
436 libxl_defbool_val(info->compression)) {
437 LOGD(ERROR, domid, "Cannot use memory checkpoint "
438 "compression in COLO mode");
439 rc = ERROR_FAIL;
440 goto out;
441 }
442
443 if (!libxl_defbool_val(info->allow_unsafe) &&
444 (libxl_defbool_val(info->blackhole) ||
445 !libxl_defbool_val(info->netbuf) ||
446 !libxl_defbool_val(info->diskbuf))) {
447 LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
448 "disable network buffering and disk replication");
449 rc = ERROR_FAIL;
450 goto out;
451 }
452
453
454 GCNEW(dss);
455 dss->ao = ao;
456 dss->callback = remus_failover_cb;
457 dss->domid = domid;
458 dss->fd = send_fd;
459 dss->recv_fd = recv_fd;
460 dss->type = type;
461 dss->live = 1;
462 dss->debug = 0;
463 dss->remus = info;
464 if (libxl_defbool_val(info->colo))
465 dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
466 else
467 dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
468
469 assert(info);
470
471 /* Point of no return */
472 if (libxl_defbool_val(info->colo))
473 libxl__colo_save_setup(egc, &dss->css);
474 else
475 libxl__remus_setup(egc, &dss->rs);
476 return AO_INPROGRESS;
477
478 out:
479 return AO_CREATE_FAIL(rc);
480 }
481
remus_failover_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)482 static void remus_failover_cb(libxl__egc *egc,
483 libxl__domain_save_state *dss, int rc)
484 {
485 STATE_AO_GC(dss->ao);
486 /*
487 * With Remus, if we reach this point, it means either
488 * backup died or some network error occurred preventing us
489 * from sending checkpoints.
490 */
491 libxl__ao_complete(egc, ao, rc);
492 }
493
domain_suspend_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)494 static void domain_suspend_cb(libxl__egc *egc,
495 libxl__domain_save_state *dss, int rc)
496 {
497 STATE_AO_GC(dss->ao);
498 int flrc;
499
500 flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
501 /* If suspend has failed already then report that error not this one. */
502 if (flrc && !rc) rc = flrc;
503
504 libxl__ao_complete(egc,ao,rc);
505
506 }
507
libxl_domain_suspend(libxl_ctx * ctx,uint32_t domid,int fd,int flags,const libxl_asyncop_how * ao_how)508 int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
509 const libxl_asyncop_how *ao_how)
510 {
511 AO_CREATE(ctx, domid, ao_how);
512 int rc;
513
514 libxl_domain_type type = libxl__domain_type(gc, domid);
515 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
516 rc = ERROR_FAIL;
517 goto out_err;
518 }
519
520 libxl__domain_save_state *dss;
521 GCNEW(dss);
522
523 dss->ao = ao;
524 dss->callback = domain_suspend_cb;
525
526 dss->domid = domid;
527 dss->fd = fd;
528 dss->type = type;
529 dss->live = flags & LIBXL_SUSPEND_LIVE;
530 dss->debug = flags & LIBXL_SUSPEND_DEBUG;
531 dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
532
533 rc = libxl__fd_flags_modify_save(gc, dss->fd,
534 ~(O_NONBLOCK|O_NDELAY), 0,
535 &dss->fdfl);
536 if (rc < 0) goto out_err;
537
538 libxl__domain_save(egc, dss);
539 return AO_INPROGRESS;
540
541 out_err:
542 return AO_CREATE_FAIL(rc);
543 }
544
domain_suspend_empty_cb(libxl__egc * egc,libxl__domain_suspend_state * dss,int rc)545 static void domain_suspend_empty_cb(libxl__egc *egc,
546 libxl__domain_suspend_state *dss, int rc)
547 {
548 STATE_AO_GC(dss->ao);
549 libxl__ao_complete(egc,ao,rc);
550 }
551
libxl_domain_suspend_only(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)552 int libxl_domain_suspend_only(libxl_ctx *ctx, uint32_t domid,
553 const libxl_asyncop_how *ao_how)
554 {
555 AO_CREATE(ctx, domid, ao_how);
556 libxl__domain_suspend_state *dsps;
557 int rc;
558
559 libxl_domain_type type = libxl__domain_type(gc, domid);
560 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
561 rc = ERROR_FAIL;
562 goto out_err;
563 }
564
565 GCNEW(dsps);
566 dsps->ao = ao;
567 dsps->domid = domid;
568 dsps->type = type;
569 rc = libxl__domain_suspend_init(egc, dsps, type);
570 if (rc < 0) goto out_err;
571 dsps->callback_common_done = domain_suspend_empty_cb;
572 libxl__domain_suspend(egc, dsps);
573 return AO_INPROGRESS;
574
575 out_err:
576 return AO_CREATE_FAIL(rc);
577 }
578
libxl_domain_pause(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)579 int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid,
580 const libxl_asyncop_how *ao_how)
581 {
582 AO_CREATE(ctx, domid, ao_how);
583 int r;
584 r = xc_domain_pause(ctx->xch, domid);
585 if (r < 0) {
586 LOGED(ERROR, domid, "Pausing domain");
587 return AO_CREATE_FAIL(ERROR_FAIL);
588 }
589 libxl__ao_complete(egc, ao, 0);
590 return AO_INPROGRESS;
591 }
592
libxl_domain_core_dump(libxl_ctx * ctx,uint32_t domid,const char * filename,const libxl_asyncop_how * ao_how)593 int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
594 const char *filename,
595 const libxl_asyncop_how *ao_how)
596 {
597 AO_CREATE(ctx, domid, ao_how);
598 int ret, rc;
599
600 ret = xc_domain_dumpcore(ctx->xch, domid, filename);
601 if (ret<0) {
602 LOGED(ERROR, domid, "Core dumping domain to %s", filename);
603 rc = ERROR_FAIL;
604 goto out;
605 }
606
607 rc = 0;
608 out:
609
610 libxl__ao_complete(egc, ao, rc);
611
612 return AO_INPROGRESS;
613 }
614
libxl__domain_unpause_deprecated(libxl__gc * gc,libxl_domid domid)615 int libxl__domain_unpause_deprecated(libxl__gc *gc, libxl_domid domid)
616 {
617 int r, rc;
618
619 libxl_domain_type type = libxl__domain_type(gc, domid);
620 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
621 rc = ERROR_FAIL;
622 goto out;
623 }
624
625 if (type == LIBXL_DOMAIN_TYPE_HVM) {
626 rc = libxl__domain_resume_device_model_deprecated(gc, domid);
627 if (rc < 0) {
628 LOGD(ERROR, domid,
629 "Failed to unpause device model for domain: %d", rc);
630 goto out;
631 }
632 }
633 r = xc_domain_unpause(CTX->xch, domid);
634 if (r < 0) {
635 LOGED(ERROR, domid, "Unpausing domain");
636 rc = ERROR_FAIL;
637 goto out;
638 }
639 rc = 0;
640 out:
641 return rc;
642 }
643
644 static void domain_unpause_done(libxl__egc *egc,
645 libxl__dm_resume_state *,
646 int rc);
647
libxl__domain_unpause(libxl__egc * egc,libxl__dm_resume_state * dmrs)648 void libxl__domain_unpause(libxl__egc *egc,
649 libxl__dm_resume_state *dmrs)
650 {
651 STATE_AO_GC(dmrs->ao);
652 int rc = 0;
653
654 /* Convenience aliases */
655 libxl_domid domid = dmrs->domid;
656
657 libxl_domain_type type = libxl__domain_type(gc, domid);
658 if (type == LIBXL_DOMAIN_TYPE_INVALID) {
659 rc = ERROR_FAIL;
660 goto out;
661 }
662
663 if (type == LIBXL_DOMAIN_TYPE_HVM) {
664 dmrs->dm_resumed_callback = domain_unpause_done;
665 libxl__dm_resume(egc, dmrs); /* must be last */
666 return;
667 }
668 rc = 0;
669 out:
670 domain_unpause_done(egc, dmrs, rc);
671 }
672
domain_unpause_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)673 static void domain_unpause_done(libxl__egc *egc,
674 libxl__dm_resume_state *dmrs,
675 int rc)
676 {
677 EGC_GC;
678 int r;
679
680 /* Convenience aliases */
681 libxl_domid domid = dmrs->domid;
682
683 if (rc) goto out;
684
685 r = xc_domain_unpause(CTX->xch, domid);
686 if (r < 0) {
687 LOGED(ERROR, domid, "Unpausing domain");
688 rc = ERROR_FAIL;
689 goto out;
690 }
691 rc = 0;
692 out:
693 dmrs->callback(egc, dmrs, rc);
694 }
695
696 static void domain_unpause_ao_done(libxl__egc *egc,
697 libxl__dm_resume_state *,
698 int rc);
699
libxl_domain_unpause(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)700 int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid,
701 const libxl_asyncop_how *ao_how)
702 {
703 AO_CREATE(ctx, domid, ao_how);
704 libxl__dm_resume_state *dmrs;
705
706 GCNEW(dmrs);
707 dmrs->ao = ao;
708 dmrs->domid = domid;
709 dmrs->callback = domain_unpause_ao_done;
710 libxl__domain_unpause(egc, dmrs); /* must be last */
711 return AO_INPROGRESS;
712 }
713
domain_unpause_ao_done(libxl__egc * egc,libxl__dm_resume_state * dmrs,int rc)714 static void domain_unpause_ao_done(libxl__egc *egc,
715 libxl__dm_resume_state *dmrs,
716 int rc)
717 {
718 STATE_AO_GC(dmrs->ao);
719
720 libxl__ao_complete(egc, ao, rc);
721 }
722
libxl__domain_pvcontrol_available(libxl__gc * gc,uint32_t domid)723 int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
724 {
725 libxl_ctx *ctx = libxl__gc_owner(gc);
726
727 uint64_t pvdriver = 0;
728 int ret;
729
730 libxl_domain_type domtype = libxl__domain_type(gc, domid);
731 if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
732 return ERROR_FAIL;
733
734 if (domtype != LIBXL_DOMAIN_TYPE_HVM)
735 return 1;
736
737 ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
738 if (ret<0) {
739 LOGED(ERROR, domid, "Getting HVM callback IRQ");
740 return ERROR_FAIL;
741 }
742 return !!pvdriver;
743 }
744
libxl__domain_pvcontrol_xspath(libxl__gc * gc,uint32_t domid)745 const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
746 {
747 const char *dom_path;
748
749 dom_path = libxl__xs_get_dompath(gc, domid);
750 if (!dom_path)
751 return NULL;
752
753 return GCSPRINTF("%s/control/shutdown", dom_path);
754 }
755
libxl__domain_pvcontrol_read(libxl__gc * gc,xs_transaction_t t,uint32_t domid)756 char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
757 uint32_t domid)
758 {
759 const char *shutdown_path;
760
761 shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
762 if (!shutdown_path)
763 return NULL;
764
765 return libxl__xs_read(gc, t, shutdown_path);
766 }
767
libxl__domain_pvcontrol(libxl__egc * egc,libxl__xswait_state * pvcontrol,domid_t domid,const char * cmd)768 int libxl__domain_pvcontrol(libxl__egc *egc, libxl__xswait_state *pvcontrol,
769 domid_t domid, const char *cmd)
770 {
771 STATE_AO_GC(pvcontrol->ao);
772 const char *shutdown_path;
773 int rc;
774
775 rc = libxl__domain_pvcontrol_available(gc, domid);
776 if (rc < 0)
777 return rc;
778
779 if (!rc)
780 return ERROR_NOPARAVIRT;
781
782 shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
783 if (!shutdown_path)
784 return ERROR_FAIL;
785
786 rc = libxl__xs_printf(gc, XBT_NULL, shutdown_path, "%s", cmd);
787 if (rc)
788 return rc;
789
790 pvcontrol->path = shutdown_path;
791 pvcontrol->what = GCSPRINTF("guest acknowledgement of %s request", cmd);
792 pvcontrol->timeout_ms = 60 * 1000;
793 rc = libxl__xswait_start(gc, pvcontrol);
794 if (rc)
795 return rc;
796
797 return 0;
798 }
799
pvcontrol_acked(const char * state)800 static bool pvcontrol_acked(const char *state)
801 {
802 if (!state || !strcmp(state,""))
803 return true;
804
805 return false;
806 }
807
808 /* Xenstore watch callback prototype for the reboot/poweroff operations. */
809 static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
810 const char *state);
811
libxl_domain_shutdown(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)812 int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid,
813 const libxl_asyncop_how *ao_how)
814 {
815 AO_CREATE(ctx, domid, ao_how);
816 libxl__xswait_state *pvcontrol;
817 int rc;
818
819 GCNEW(pvcontrol);
820 pvcontrol->ao = ao;
821 pvcontrol->callback = pvcontrol_cb;
822 rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "poweroff");
823
824 return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
825 }
826
libxl_domain_reboot(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)827 int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid,
828 const libxl_asyncop_how *ao_how)
829 {
830 AO_CREATE(ctx, domid, ao_how);
831 libxl__xswait_state *pvcontrol;
832 int rc;
833
834 GCNEW(pvcontrol);
835 pvcontrol->ao = ao;
836 pvcontrol->callback = pvcontrol_cb;
837 rc = libxl__domain_pvcontrol(egc, pvcontrol, domid, "reboot");
838
839 return rc ? AO_CREATE_FAIL(rc) : AO_INPROGRESS;
840 }
841
pvcontrol_cb(libxl__egc * egc,libxl__xswait_state * xswa,int rc,const char * state)842 static void pvcontrol_cb(libxl__egc *egc, libxl__xswait_state *xswa, int rc,
843 const char *state)
844 {
845 STATE_AO_GC(xswa->ao);
846
847 if (!rc && !pvcontrol_acked(state))
848 return;
849
850 libxl__xswait_stop(gc, xswa);
851
852 if (rc)
853 LOG(ERROR, "guest didn't acknowledge control request: %d", rc);
854
855 libxl__ao_complete(egc, ao, rc);
856 }
857
domain_death_occurred(libxl__egc * egc,libxl_evgen_domain_death ** evg_upd,const char * why)858 static void domain_death_occurred(libxl__egc *egc,
859 libxl_evgen_domain_death **evg_upd,
860 const char *why) {
861 /* Removes **evg_upd from death_list and puts it on death_reported
862 * and advances *evg_upd to the next entry.
863 * Call sites in domain_death_xswatch_callback must use "continue". */
864 EGC_GC;
865 libxl_evgen_domain_death *const evg = *evg_upd;
866
867 LOGD(DEBUG, evg->domid, "%s", why);
868
869 libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
870 *evg_upd = evg_next;
871
872 libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
873
874 libxl__event_occurred(egc, ev);
875
876 evg->death_reported = 1;
877 LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
878 LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
879 }
880
domain_death_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * w,const char * wpath,const char * epath)881 static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
882 const char *wpath, const char *epath) {
883 EGC_GC;
884 libxl_evgen_domain_death *evg;
885 int rc;
886
887 CTX_LOCK;
888
889 evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
890
891 for (;;) {
892 if (!evg) goto out;
893
894 int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
895 xc_domaininfo_t domaininfos[nentries];
896 const xc_domaininfo_t *got = domaininfos, *gotend;
897
898 rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
899 if (rc == -1) {
900 LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
901 " processing @releaseDomain watch event",
902 errno, 0);
903 goto out;
904 }
905 gotend = &domaininfos[rc];
906
907 LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
908 evg, nentries, rc,
909 rc>0 ? (long)domaininfos[0].domain : 0,
910 rc>0 ? (long)domaininfos[rc-1].domain : 0);
911
912 for (;;) {
913 if (!evg) {
914 LOG(DEBUG, "[evg=0] all reported");
915 goto all_reported;
916 }
917
918 LOGD(DEBUG, evg->domid, "[evg=%p]"
919 " got=domaininfos[%d] got->domain=%ld",
920 evg, (int)(got - domaininfos),
921 got < gotend ? (long)got->domain : -1L);
922
923 if (!rc) {
924 domain_death_occurred(egc, &evg, "empty list");
925 continue;
926 }
927
928 if (got == gotend) {
929 LOG(DEBUG, " got==gotend");
930 break;
931 }
932
933 if (got->domain > evg->domid) {
934 /* ie, the list doesn't contain evg->domid any more so
935 * the domain has been destroyed */
936 domain_death_occurred(egc, &evg, "missing from list");
937 continue;
938 }
939
940 if (got->domain < evg->domid) {
941 got++;
942 continue;
943 }
944
945 assert(evg->domid == got->domain);
946 LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
947 evg->shutdown_reported, got->flags);
948
949 if (got->flags & XEN_DOMINF_dying) {
950 domain_death_occurred(egc, &evg, "dying");
951 continue;
952 }
953
954 if (!evg->shutdown_reported &&
955 (got->flags & XEN_DOMINF_shutdown)) {
956 libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
957 got->domain, evg->user);
958
959 LOG(DEBUG, " shutdown reporting");
960
961 ev->u.domain_shutdown.shutdown_reason =
962 (got->flags >> XEN_DOMINF_shutdownshift) &
963 XEN_DOMINF_shutdownmask;
964 libxl__event_occurred(egc, ev);
965
966 evg->shutdown_reported = 1;
967 }
968 evg = LIBXL_TAILQ_NEXT(evg, entry);
969 }
970
971 assert(rc); /* rc==0 results in us eating all evgs and quitting */
972 }
973 all_reported:
974 out:
975
976 LOG(DEBUG, "domain death search done");
977
978 CTX_UNLOCK;
979 }
980
libxl_evenable_domain_death(libxl_ctx * ctx,uint32_t domid,libxl_ev_user user,libxl_evgen_domain_death ** evgen_out)981 int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
982 libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
983 GC_INIT(ctx);
984 libxl_evgen_domain_death *evg, *evg_search;
985 int rc;
986
987 CTX_LOCK;
988
989 evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
990 memset(evg, 0, sizeof(*evg));
991 evg->domid = domid;
992 evg->user = user;
993
994 LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
995 evg->domid > evg_search->domid);
996
997 if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
998 rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
999 domain_death_xswatch_callback, "@releaseDomain");
1000 if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
1001 }
1002
1003 *evgen_out = evg;
1004 rc = 0;
1005
1006 out:
1007 CTX_UNLOCK;
1008 GC_FREE;
1009 return rc;
1010 };
1011
libxl__evdisable_domain_death(libxl__gc * gc,libxl_evgen_domain_death * evg)1012 void libxl__evdisable_domain_death(libxl__gc *gc,
1013 libxl_evgen_domain_death *evg) {
1014 CTX_LOCK;
1015
1016 if (!evg->death_reported)
1017 LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
1018 else
1019 LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
1020
1021 free(evg);
1022
1023 if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
1024 libxl__ev_xswatch_isregistered(&CTX->death_watch))
1025 libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
1026
1027 CTX_UNLOCK;
1028 }
1029
libxl_evdisable_domain_death(libxl_ctx * ctx,libxl_evgen_domain_death * evg)1030 void libxl_evdisable_domain_death(libxl_ctx *ctx,
1031 libxl_evgen_domain_death *evg) {
1032 GC_INIT(ctx);
1033 libxl__evdisable_domain_death(gc, evg);
1034 GC_FREE;
1035 }
1036
1037 /* Callbacks for libxl_domain_destroy */
1038
1039 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
1040 int rc);
1041
libxl_domain_destroy(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)1042 int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
1043 const libxl_asyncop_how *ao_how)
1044 {
1045 AO_CREATE(ctx, domid, ao_how);
1046 libxl__domain_destroy_state *dds;
1047
1048 GCNEW(dds);
1049 dds->ao = ao;
1050 dds->domid = domid;
1051 dds->callback = domain_destroy_cb;
1052 libxl__domain_destroy(egc, dds);
1053
1054 return AO_INPROGRESS;
1055 }
1056
domain_destroy_cb(libxl__egc * egc,libxl__domain_destroy_state * dds,int rc)1057 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
1058 int rc)
1059 {
1060 STATE_AO_GC(dds->ao);
1061
1062 if (rc)
1063 LOGD(ERROR, dds->domid, "Destruction of domain failed");
1064
1065 libxl__ao_complete(egc, ao, rc);
1066 }
1067
1068 /* Callbacks for libxl__domain_destroy */
1069
1070 static void stubdom_destroy_callback(libxl__egc *egc,
1071 libxl__destroy_domid_state *dis,
1072 int rc);
1073
1074 static void domain_destroy_callback(libxl__egc *egc,
1075 libxl__destroy_domid_state *dis,
1076 int rc);
1077
1078 static void destroy_finish_check(libxl__egc *egc,
1079 libxl__domain_destroy_state *dds);
1080
libxl__domain_destroy(libxl__egc * egc,libxl__domain_destroy_state * dds)1081 void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
1082 {
1083 STATE_AO_GC(dds->ao);
1084 uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
1085
1086 if (stubdomid) {
1087 dds->stubdom.ao = ao;
1088 dds->stubdom.domid = stubdomid;
1089 dds->stubdom.callback = stubdom_destroy_callback;
1090 dds->stubdom.soft_reset = false;
1091 libxl__destroy_domid(egc, &dds->stubdom);
1092 } else {
1093 dds->stubdom_finished = 1;
1094 }
1095
1096 dds->domain.ao = ao;
1097 dds->domain.domid = dds->domid;
1098 dds->domain.callback = domain_destroy_callback;
1099 dds->domain.soft_reset = dds->soft_reset;
1100 libxl__destroy_domid(egc, &dds->domain);
1101 }
1102
stubdom_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)1103 static void stubdom_destroy_callback(libxl__egc *egc,
1104 libxl__destroy_domid_state *dis,
1105 int rc)
1106 {
1107 STATE_AO_GC(dis->ao);
1108 libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
1109 const char *savefile;
1110
1111 if (rc) {
1112 LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
1113 dis->domid);
1114 dds->rc = rc;
1115 }
1116
1117 dds->stubdom_finished = 1;
1118 savefile = libxl__device_model_savefile(gc, dis->domid);
1119 rc = libxl__remove_file(gc, savefile);
1120 if (rc) {
1121 LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
1122 savefile);
1123 }
1124
1125 destroy_finish_check(egc, dds);
1126 }
1127
domain_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)1128 static void domain_destroy_callback(libxl__egc *egc,
1129 libxl__destroy_domid_state *dis,
1130 int rc)
1131 {
1132 STATE_AO_GC(dis->ao);
1133 libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
1134
1135 if (rc) {
1136 LOGD(ERROR, dis->domid, "Unable to destroy guest");
1137 dds->rc = rc;
1138 }
1139
1140 dds->domain_finished = 1;
1141 destroy_finish_check(egc, dds);
1142 }
1143
destroy_finish_check(libxl__egc * egc,libxl__domain_destroy_state * dds)1144 static void destroy_finish_check(libxl__egc *egc,
1145 libxl__domain_destroy_state *dds)
1146 {
1147 if (!(dds->domain_finished && dds->stubdom_finished))
1148 return;
1149
1150 dds->callback(egc, dds, dds->rc);
1151 }
1152
1153 /* Callbacks for libxl__destroy_domid */
1154 static void destroy_domid_pci_done(libxl__egc *egc,
1155 libxl__multidev *multidev,
1156 int rc);
1157 static void dm_destroy_cb(libxl__egc *egc,
1158 libxl__destroy_devicemodel_state *ddms,
1159 int rc);
1160
1161 static void devices_destroy_cb(libxl__egc *egc,
1162 libxl__devices_remove_state *drs,
1163 int rc);
1164
1165 static void domain_destroy_domid_cb(libxl__egc *egc,
1166 libxl__ev_child *destroyer,
1167 pid_t pid, int status);
1168
libxl__destroy_domid(libxl__egc * egc,libxl__destroy_domid_state * dis)1169 void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
1170 {
1171 STATE_AO_GC(dis->ao);
1172 uint32_t domid = dis->domid;
1173 int rc;
1174
1175 libxl__ev_child_init(&dis->destroyer);
1176
1177 rc = libxl_domain_info(CTX, NULL, domid);
1178 switch(rc) {
1179 case 0:
1180 break;
1181 case ERROR_DOMAIN_NOTFOUND:
1182 LOGD(ERROR, domid, "Non-existant domain");
1183 default:
1184 goto out;
1185 }
1186
1187 libxl__multidev_begin(ao, &dis->multidev);
1188 dis->multidev.callback = destroy_domid_pci_done;
1189 libxl__device_pci_destroy_all(egc, domid, &dis->multidev);
1190 libxl__multidev_prepared(egc, &dis->multidev, 0);
1191 return;
1192
1193 out:
1194 assert(rc);
1195 dis->callback(egc, dis, rc);
1196 }
1197
destroy_domid_pci_done(libxl__egc * egc,libxl__multidev * multidev,int rc)1198 static void destroy_domid_pci_done(libxl__egc *egc,
1199 libxl__multidev *multidev,
1200 int rc)
1201 {
1202 STATE_AO_GC(multidev->ao);
1203 libxl__destroy_domid_state *dis =
1204 CONTAINER_OF(multidev, *dis, multidev);
1205 int dm_present;
1206 int r;
1207
1208 /* Convenience aliases */
1209 libxl_domid domid = dis->domid;
1210
1211 if (rc) {
1212 LOGD(ERROR, domid, "Pci shutdown failed");
1213 goto out;
1214 }
1215
1216 r = xc_domain_pause(CTX->xch, domid);
1217 if (r < 0) {
1218 LOGEVD(ERROR, r, domid, "xc_domain_pause failed");
1219 rc = ERROR_FAIL;
1220 }
1221
1222 switch (libxl__domain_type(gc, domid)) {
1223 case LIBXL_DOMAIN_TYPE_HVM:
1224 if (libxl_get_stubdom_id(CTX, domid)) {
1225 dm_present = 0;
1226 break;
1227 }
1228 /* fall through */
1229 case LIBXL_DOMAIN_TYPE_PVH:
1230 case LIBXL_DOMAIN_TYPE_PV:
1231 dm_present = libxl__dm_active(gc, domid);
1232 break;
1233 case LIBXL_DOMAIN_TYPE_INVALID:
1234 rc = ERROR_FAIL;
1235 goto out;
1236 default:
1237 abort();
1238 }
1239
1240 if (dm_present) {
1241 dis->ddms.ao = ao;
1242 dis->ddms.domid = domid;
1243 dis->ddms.callback = dm_destroy_cb;
1244
1245 libxl__destroy_device_model(egc, &dis->ddms);
1246 return;
1247 } else {
1248 dm_destroy_cb(egc, &dis->ddms, 0);
1249 return;
1250 }
1251
1252 out:
1253 assert(rc);
1254 dis->callback(egc, dis, rc);
1255 return;
1256 }
1257
dm_destroy_cb(libxl__egc * egc,libxl__destroy_devicemodel_state * ddms,int rc)1258 static void dm_destroy_cb(libxl__egc *egc,
1259 libxl__destroy_devicemodel_state *ddms,
1260 int rc)
1261 {
1262 libxl__destroy_domid_state *dis = CONTAINER_OF(ddms, *dis, ddms);
1263 STATE_AO_GC(dis->ao);
1264 uint32_t domid = dis->domid;
1265 uint32_t target_domid;
1266
1267 if (rc < 0)
1268 LOGD(ERROR, domid, "libxl__destroy_device_model failed");
1269
1270 if (libxl_is_stubdom(CTX, domid, &target_domid) &&
1271 libxl__stubdomain_is_linux_running(gc, target_domid)) {
1272 char *path = GCSPRINTF("/local/domain/%d/image/qmp-proxy-pid", domid);
1273
1274 libxl__kill_xs_path(gc, path, "QMP Proxy");
1275 /* qmp-proxy for stubdom registers target_domid's QMP sockets. */
1276 libxl__qmp_cleanup(gc, target_domid);
1277 }
1278
1279 dis->drs.ao = ao;
1280 dis->drs.domid = domid;
1281 dis->drs.callback = devices_destroy_cb;
1282 dis->drs.force = 1;
1283 libxl__devices_destroy(egc, &dis->drs);
1284 }
1285
libxl__get_domid_reuse_timeout(void)1286 static unsigned int libxl__get_domid_reuse_timeout(void)
1287 {
1288 const char *env_timeout = getenv("LIBXL_DOMID_REUSE_TIMEOUT");
1289
1290 return env_timeout ? strtol(env_timeout, NULL, 0) :
1291 LIBXL_DOMID_REUSE_TIMEOUT;
1292 }
1293
libxl__domid_history_path(libxl__gc * gc,const char * suffix)1294 char *libxl__domid_history_path(libxl__gc *gc, const char *suffix)
1295 {
1296 return GCSPRINTF("%s/domid-history%s", libxl__run_dir_path(),
1297 suffix ?: "");
1298 }
1299
libxl_clear_domid_history(libxl_ctx * ctx)1300 int libxl_clear_domid_history(libxl_ctx *ctx)
1301 {
1302 GC_INIT(ctx);
1303 char *path;
1304 int rc = ERROR_FAIL;
1305
1306 path = libxl__domid_history_path(gc, NULL);
1307 if (!path)
1308 goto out;
1309
1310 if (unlink(path) < 0 && errno != ENOENT) {
1311 LOGE(ERROR, "failed to remove '%s'\n", path);
1312 goto out;
1313 }
1314
1315 rc = 0;
1316
1317 out:
1318 GC_FREE;
1319 return rc;
1320 }
1321
1322 struct libxl__domid_history {
1323 long timeout;
1324 char *path;
1325 FILE *f;
1326 struct timespec ts;
1327 };
1328
libxl__domid_history_dispose(struct libxl__domid_history * ctxt)1329 static void libxl__domid_history_dispose(
1330 struct libxl__domid_history *ctxt)
1331 {
1332 if (ctxt->f) {
1333 fclose(ctxt->f);
1334 ctxt->f = NULL;
1335 }
1336 }
1337
libxl__open_domid_history(libxl__gc * gc,struct libxl__domid_history * ctxt)1338 static int libxl__open_domid_history(libxl__gc *gc,
1339 struct libxl__domid_history *ctxt)
1340 {
1341 ctxt->timeout = libxl__get_domid_reuse_timeout();
1342 ctxt->path = libxl__domid_history_path(gc, NULL);
1343
1344 ctxt->f = fopen(ctxt->path, "r");
1345 if (!ctxt->f && errno != ENOENT) {
1346 LOGE(ERROR, "failed to open '%s'", ctxt->path);
1347 return ERROR_FAIL;
1348 }
1349
1350 if (clock_gettime(CLOCK_MONOTONIC, &ctxt->ts)) {
1351 LOGE(ERROR, "failed to get time");
1352 libxl__domid_history_dispose(ctxt);
1353 return ERROR_FAIL;
1354 }
1355
1356 return 0;
1357 }
1358
libxl__close_domid_history(libxl__gc * gc,struct libxl__domid_history * ctxt)1359 static int libxl__close_domid_history(libxl__gc *gc,
1360 struct libxl__domid_history *ctxt)
1361 {
1362 int r;
1363
1364 if (!ctxt->f) return 0;
1365
1366 r = fclose(ctxt->f);
1367 ctxt->f = NULL;
1368 if (r == EOF) {
1369 LOGE(ERROR, "failed to close '%s'", ctxt->path);
1370 return ERROR_FAIL;
1371 }
1372
1373 return 0;
1374 }
1375
libxl__read_recent(libxl__gc * gc,struct libxl__domid_history * ctxt,unsigned long * sec,unsigned int * domid)1376 static int libxl__read_recent(libxl__gc *gc,
1377 struct libxl__domid_history *ctxt,
1378 unsigned long *sec, unsigned int *domid)
1379 {
1380 if (!ctxt->f) {
1381 *domid = INVALID_DOMID;
1382 return 0;
1383 }
1384
1385 for (;;) {
1386 int r = fscanf(ctxt->f, "%lu %u", sec, domid);
1387
1388 if (r == EOF) {
1389 if (ferror(ctxt->f)) {
1390 LOGE(ERROR, "failed to read from '%s'", ctxt->path);
1391 return ERROR_FAIL;
1392 }
1393
1394 *domid = INVALID_DOMID;
1395 break;
1396 } else if (r == 2 && libxl_domid_valid_guest(*domid) &&
1397 ctxt->ts.tv_sec - *sec <= ctxt->timeout) {
1398 break;
1399 }
1400 }
1401
1402 return 0;
1403 }
1404
libxl__mark_domid_recent(libxl__gc * gc,uint32_t domid)1405 static int libxl__mark_domid_recent(libxl__gc *gc, uint32_t domid)
1406 {
1407 libxl__flock *lock;
1408 struct libxl__domid_history ctxt = {};
1409 char *new;
1410 FILE *nf = NULL;
1411 int r, rc;
1412
1413 lock = libxl__lock_domid_history(gc);
1414 if (!lock) {
1415 LOGED(ERROR, domid, "failed to acquire lock");
1416 rc = ERROR_FAIL;
1417 goto out;
1418 }
1419
1420 rc = libxl__open_domid_history(gc, &ctxt);
1421 if (rc) goto out;
1422
1423 new = libxl__domid_history_path(gc, ".new");
1424 nf = fopen(new, "a");
1425 if (!nf) {
1426 LOGED(ERROR, domid, "failed to open '%s'", new);
1427 goto out;
1428 }
1429
1430 for (;;) {
1431 unsigned long sec;
1432 unsigned int val;
1433
1434 rc = libxl__read_recent(gc, &ctxt, &sec, &val);
1435 if (rc) goto out;
1436
1437 if (val == INVALID_DOMID) /* EOF */
1438 break;
1439
1440 r = fprintf(nf, "%lu %u\n", sec, val);
1441 if (r < 0) {
1442 LOGED(ERROR, domid, "failed to write to '%s'", new);
1443 goto out;
1444 }
1445 }
1446
1447 r = fprintf(nf, "%lu %u\n", ctxt.ts.tv_sec, domid);
1448 if (r < 0) {
1449 LOGED(ERROR, domid, "failed to write to '%s'", new);
1450 goto out;
1451 }
1452
1453 r = fclose(nf);
1454 nf = NULL;
1455 if (r == EOF) {
1456 LOGED(ERROR, domid, "failed to close '%s'", new);
1457 goto out;
1458 }
1459
1460 rc = libxl__close_domid_history(gc, &ctxt);
1461 if (rc) goto out;
1462
1463 r = rename(new, ctxt.path);
1464 if (r) {
1465 LOGE(ERROR, "failed to rename '%s' -> '%s'", new, ctxt.path);
1466 return ERROR_FAIL;
1467 }
1468
1469 out:
1470 if (nf) fclose(nf);
1471 libxl__domid_history_dispose(&ctxt);
1472 if (lock) libxl__unlock_file(lock);
1473
1474 return rc;
1475 }
1476
libxl__is_domid_recent(libxl__gc * gc,uint32_t domid,bool * recent)1477 int libxl__is_domid_recent(libxl__gc *gc, uint32_t domid, bool *recent)
1478 {
1479 struct libxl__domid_history ctxt = {};
1480 int rc;
1481
1482 rc = libxl__open_domid_history(gc, &ctxt);
1483 if (rc) goto out;
1484
1485 *recent = false;
1486 for (;;) {
1487 unsigned long sec;
1488 unsigned int val;
1489
1490 rc = libxl__read_recent(gc, &ctxt, &sec, &val);
1491 if (rc) goto out;
1492
1493 if (val == INVALID_DOMID) /* EOF */
1494 break;
1495
1496 if (val == domid && ctxt.ts.tv_sec - sec <= ctxt.timeout) {
1497 *recent = true;
1498 break;
1499 }
1500 }
1501
1502 rc = libxl__close_domid_history(gc, &ctxt);
1503
1504 out:
1505 libxl__domid_history_dispose(&ctxt);
1506
1507 return rc;
1508 }
1509
devices_destroy_cb(libxl__egc * egc,libxl__devices_remove_state * drs,int rc)1510 static void devices_destroy_cb(libxl__egc *egc,
1511 libxl__devices_remove_state *drs,
1512 int rc)
1513 {
1514 STATE_AO_GC(drs->ao);
1515 libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
1516 libxl_ctx *ctx = CTX;
1517 uint32_t domid = dis->domid;
1518 char *dom_path;
1519 char *vm_path;
1520 libxl__flock *lock;
1521
1522 dom_path = libxl__xs_get_dompath(gc, domid);
1523 if (!dom_path) {
1524 rc = ERROR_FAIL;
1525 goto out;
1526 }
1527
1528 if (rc < 0)
1529 LOGD(ERROR, domid, "libxl__devices_destroy failed");
1530
1531 vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
1532 if (vm_path)
1533 if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
1534 LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
1535
1536 if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
1537 LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
1538
1539 xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
1540 xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
1541
1542 /* This is async operation, we already hold CTX lock */
1543 lock = libxl__lock_domain_userdata(gc, domid);
1544 if (!lock) {
1545 rc = ERROR_LOCK_FAIL;
1546 goto out;
1547 }
1548 libxl__userdata_destroyall(gc, domid);
1549
1550 libxl__unlock_file(lock);
1551
1552 /* Clean up qemu-save and qemu-resume files. They are
1553 * intermediate files created by libxc. Unfortunately they
1554 * don't fit in existing userdata scheme very well. In soft reset
1555 * case we need to keep the file.
1556 */
1557 if (!dis->soft_reset) {
1558 rc = libxl__remove_file(gc,
1559 libxl__device_model_savefile(gc, domid));
1560 if (rc < 0) goto out;
1561 }
1562 rc = libxl__remove_file(gc,
1563 GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
1564 if (rc < 0) goto out;
1565
1566 rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
1567 if (rc < 0) goto out;
1568 if (!rc) { /* child */
1569 ctx->xch = xc_interface_open(ctx->lg,0,0);
1570 if (!ctx->xch) goto badchild;
1571
1572 if (!dis->soft_reset) {
1573 rc = libxl__mark_domid_recent(gc, domid);
1574 if (rc) goto badchild;
1575 rc = xc_domain_destroy(ctx->xch, domid);
1576 } else {
1577 rc = xc_domain_pause(ctx->xch, domid);
1578 if (rc < 0) goto badchild;
1579 rc = xc_domain_soft_reset(ctx->xch, domid);
1580 if (rc < 0) goto badchild;
1581 rc = xc_domain_unpause(ctx->xch, domid);
1582 }
1583 if (rc < 0) goto badchild;
1584 _exit(0);
1585
1586 badchild:
1587 if (errno > 0 && errno < 126) {
1588 _exit(errno);
1589 } else {
1590 LOGED(ERROR, domid,
1591 "xc_domain_destroy failed (with difficult errno value %d)",
1592 errno);
1593 _exit(-1);
1594 }
1595 }
1596 LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
1597
1598 return;
1599
1600 out:
1601 dis->callback(egc, dis, rc);
1602 return;
1603 }
1604
domain_destroy_domid_cb(libxl__egc * egc,libxl__ev_child * destroyer,pid_t pid,int status)1605 static void domain_destroy_domid_cb(libxl__egc *egc,
1606 libxl__ev_child *destroyer,
1607 pid_t pid, int status)
1608 {
1609 libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
1610 STATE_AO_GC(dis->ao);
1611 int rc;
1612
1613 if (status) {
1614 if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
1615 LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
1616 "xc_domain_destroy failed");
1617 } else {
1618 libxl_report_child_exitstatus(CTX, XTL_ERROR,
1619 "async domain destroy", pid, status);
1620 }
1621 rc = ERROR_FAIL;
1622 goto out;
1623 }
1624 rc = 0;
1625
1626 out:
1627 dis->callback(egc, dis, rc);
1628 }
1629
libxl__get_domid(libxl__gc * gc,uint32_t * domid)1630 int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
1631 {
1632 int rc;
1633 const char *xs_domid;
1634
1635 rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
1636 if (rc) goto out;
1637 if (!xs_domid) {
1638 LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
1639 rc = ERROR_FAIL;
1640 goto out;
1641 }
1642
1643 *domid = atoi(xs_domid);
1644
1645 out:
1646 return rc;
1647 }
1648
libxl__resolve_domid(libxl__gc * gc,const char * name,uint32_t * domid)1649 int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
1650 {
1651 if (!name)
1652 return 0;
1653 return libxl_domain_qualifier_to_domid(CTX, name, domid);
1654 }
1655
libxl_list_vcpu(libxl_ctx * ctx,uint32_t domid,int * nr_vcpus_out,int * nr_cpus_out)1656 libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
1657 int *nr_vcpus_out, int *nr_cpus_out)
1658 {
1659 GC_INIT(ctx);
1660 libxl_vcpuinfo *ptr, *ret;
1661 xc_domaininfo_t domaininfo;
1662 xc_vcpuinfo_t vcpuinfo;
1663
1664 if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
1665 LOGED(ERROR, domid, "Getting infolist");
1666 GC_FREE;
1667 return NULL;
1668 }
1669
1670 if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
1671 GC_FREE;
1672 return NULL;
1673 }
1674
1675 *nr_cpus_out = libxl_get_max_cpus(ctx);
1676 ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
1677 sizeof(libxl_vcpuinfo));
1678
1679 for (*nr_vcpus_out = 0;
1680 *nr_vcpus_out <= domaininfo.max_vcpu_id;
1681 ++*nr_vcpus_out, ++ptr) {
1682 libxl_bitmap_init(&ptr->cpumap);
1683 if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
1684 goto err;
1685 libxl_bitmap_init(&ptr->cpumap_soft);
1686 if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
1687 goto err;
1688 if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
1689 LOGED(ERROR, domid, "Getting vcpu info");
1690 goto err;
1691 }
1692
1693 if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
1694 ptr->cpumap.map, ptr->cpumap_soft.map,
1695 XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
1696 LOGED(ERROR, domid, "Getting vcpu affinity");
1697 goto err;
1698 }
1699 ptr->vcpuid = *nr_vcpus_out;
1700 ptr->cpu = vcpuinfo.cpu;
1701 ptr->online = !!vcpuinfo.online;
1702 ptr->blocked = !!vcpuinfo.blocked;
1703 ptr->running = !!vcpuinfo.running;
1704 ptr->vcpu_time = vcpuinfo.cpu_time;
1705 }
1706 GC_FREE;
1707 return ret;
1708
1709 err:
1710 libxl_bitmap_dispose(&ptr->cpumap);
1711 libxl_bitmap_dispose(&ptr->cpumap_soft);
1712 free(ret);
1713 GC_FREE;
1714 return NULL;
1715 }
1716
libxl__set_vcpuonline_xenstore(libxl__gc * gc,uint32_t domid,const libxl_bitmap * cpumap,const libxl_dominfo * info)1717 static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
1718 const libxl_bitmap *cpumap,
1719 const libxl_dominfo *info)
1720 {
1721 char *dompath;
1722 xs_transaction_t t;
1723 int i, rc = ERROR_FAIL;
1724
1725 if (!(dompath = libxl__xs_get_dompath(gc, domid)))
1726 goto out;
1727
1728 retry_transaction:
1729 t = xs_transaction_start(CTX->xsh);
1730 for (i = 0; i <= info->vcpu_max_id; i++)
1731 libxl__xs_printf(gc, t,
1732 GCSPRINTF("%s/cpu/%u/availability", dompath, i),
1733 "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
1734 if (!xs_transaction_end(CTX->xsh, t, 0)) {
1735 if (errno == EAGAIN)
1736 goto retry_transaction;
1737 } else
1738 rc = 0;
1739 out:
1740 return rc;
1741 }
1742
qmp_parse_query_cpus(libxl__gc * gc,libxl_domid domid,const libxl__json_object * response,libxl_bitmap * const map)1743 static int qmp_parse_query_cpus(libxl__gc *gc,
1744 libxl_domid domid,
1745 const libxl__json_object *response,
1746 libxl_bitmap *const map)
1747 {
1748 int i;
1749 const libxl__json_object *cpu;
1750
1751 libxl_bitmap_set_none(map);
1752 /* Parse response to QMP command "query-cpus":
1753 * [ { 'CPU': 'int',...} ]
1754 */
1755 for (i = 0; (cpu = libxl__json_array_get(response, i)); i++) {
1756 unsigned int cpu_index;
1757 const libxl__json_object *o;
1758
1759 o = libxl__json_map_get("CPU", cpu, JSON_INTEGER);
1760 if (!o) {
1761 LOGD(ERROR, domid, "Failed to retrieve CPU index.");
1762 return ERROR_QEMU_API;
1763 }
1764
1765 cpu_index = libxl__json_object_get_integer(o);
1766 libxl_bitmap_set(map, cpu_index);
1767 }
1768
1769 return 0;
1770 }
1771
1772 typedef struct set_vcpuonline_state {
1773 libxl__ev_qmp qmp;
1774 libxl__ev_time timeout;
1775 const libxl_bitmap *cpumap;
1776 libxl_dominfo info;
1777 libxl_bitmap final_map;
1778 int index; /* for loop on final_map */
1779 } set_vcpuonline_state;
1780
1781 static void set_vcpuonline_qmp_cpus_queried(libxl__egc *,
1782 libxl__ev_qmp *, const libxl__json_object *, int rc);
1783 static void set_vcpuonline_qmp_add_cpu(libxl__egc *,
1784 libxl__ev_qmp *, const libxl__json_object *response, int rc);
1785 static void set_vcpuonline_timeout(libxl__egc *egc,
1786 libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
1787 static void set_vcpuonline_done(libxl__egc *egc,
1788 set_vcpuonline_state *svos, int rc);
1789
libxl_set_vcpuonline(libxl_ctx * ctx,uint32_t domid,libxl_bitmap * cpumap,const libxl_asyncop_how * ao_how)1790 int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid,
1791 libxl_bitmap *cpumap,
1792 const libxl_asyncop_how *ao_how)
1793 {
1794 AO_CREATE(ctx, domid, ao_how);
1795 int rc, maxcpus;
1796 set_vcpuonline_state *svos;
1797
1798 GCNEW(svos);
1799 libxl__ev_qmp_init(&svos->qmp);
1800 svos->qmp.ao = ao;
1801 svos->qmp.domid = domid;
1802 svos->qmp.payload_fd = -1;
1803 libxl__ev_time_init(&svos->timeout);
1804 svos->cpumap = cpumap;
1805 libxl_dominfo_init(&svos->info);
1806 libxl_bitmap_init(&svos->final_map);
1807
1808 /* Convenience aliases */
1809 libxl_dominfo *info = &svos->info;
1810 libxl__ev_qmp *qmp = &svos->qmp;
1811
1812 rc = libxl_domain_info(CTX, info, domid);
1813 if (rc < 0) {
1814 LOGED(ERROR, domid, "Getting domain info list");
1815 goto out;
1816 }
1817
1818 maxcpus = libxl_bitmap_count_set(cpumap);
1819 if (maxcpus == 0)
1820 {
1821 LOGED(ERROR, domid, "Requested 0 VCPUs!");
1822 rc = ERROR_FAIL;
1823 goto out;
1824 }
1825 if (maxcpus > info->vcpu_max_id + 1)
1826 {
1827 LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
1828 maxcpus, info->vcpu_max_id + 1);
1829 rc = ERROR_FAIL;
1830 goto out;
1831 }
1832
1833 switch (libxl__domain_type(gc, domid)) {
1834 case LIBXL_DOMAIN_TYPE_HVM:
1835 switch (libxl__device_model_version_running(gc, domid)) {
1836 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1837 break;
1838 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1839 rc = libxl__ev_time_register_rel(ao, &svos->timeout,
1840 set_vcpuonline_timeout,
1841 LIBXL_QMP_CMD_TIMEOUT * 1000);
1842 if (rc) goto out;
1843 qmp->callback = set_vcpuonline_qmp_cpus_queried;
1844 rc = libxl__ev_qmp_send(egc, qmp, "query-cpus", NULL);
1845 if (rc) goto out;
1846 return AO_INPROGRESS;
1847 default:
1848 rc = ERROR_INVAL;
1849 }
1850 break;
1851 case LIBXL_DOMAIN_TYPE_PVH:
1852 case LIBXL_DOMAIN_TYPE_PV:
1853 break;
1854 default:
1855 rc = ERROR_INVAL;
1856 }
1857
1858 out:
1859 set_vcpuonline_done(egc, svos, rc); /* must be last */
1860 return AO_INPROGRESS;
1861 }
1862
set_vcpuonline_qmp_cpus_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)1863 static void set_vcpuonline_qmp_cpus_queried(libxl__egc *egc,
1864 libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
1865 {
1866 EGC_GC;
1867 set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1868 int i;
1869 libxl_bitmap current_map;
1870
1871 /* Convenience aliases */
1872 libxl_bitmap *final_map = &svos->final_map;
1873
1874 libxl_bitmap_init(¤t_map);
1875
1876 if (rc) goto out;
1877
1878 libxl_bitmap_alloc(CTX, ¤t_map, svos->info.vcpu_max_id + 1);
1879 rc = qmp_parse_query_cpus(gc, qmp->domid, response, ¤t_map);
1880 if (rc) goto out;
1881
1882 libxl_bitmap_copy_alloc(CTX, final_map, svos->cpumap);
1883
1884 libxl_for_each_set_bit(i, current_map) {
1885 libxl_bitmap_reset(final_map, i);
1886 }
1887
1888 out:
1889 libxl_bitmap_dispose(¤t_map);
1890 svos->index = -1;
1891 set_vcpuonline_qmp_add_cpu(egc, qmp, NULL, rc); /* must be last */
1892 }
1893
set_vcpuonline_qmp_add_cpu(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)1894 static void set_vcpuonline_qmp_add_cpu(libxl__egc *egc,
1895 libxl__ev_qmp *qmp, const libxl__json_object *response, int rc)
1896 {
1897 STATE_AO_GC(qmp->ao);
1898 set_vcpuonline_state *svos = CONTAINER_OF(qmp, *svos, qmp);
1899 libxl__json_object *args = NULL;
1900
1901 /* Convenience aliases */
1902 libxl_bitmap *map = &svos->final_map;
1903
1904 if (rc) goto out;
1905
1906 while (libxl_bitmap_cpu_valid(map, ++svos->index)) {
1907 if (libxl_bitmap_test(map, svos->index)) {
1908 qmp->callback = set_vcpuonline_qmp_add_cpu;
1909 libxl__qmp_param_add_integer(gc, &args, "id", svos->index);
1910 rc = libxl__ev_qmp_send(egc, qmp, "cpu-add", args);
1911 if (rc) goto out;
1912 return;
1913 }
1914 }
1915
1916 out:
1917 set_vcpuonline_done(egc, svos, rc);
1918 }
1919
set_vcpuonline_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)1920 static void set_vcpuonline_timeout(libxl__egc *egc, libxl__ev_time *ev,
1921 const struct timeval *requested_abs,
1922 int rc)
1923 {
1924 EGC_GC;
1925 set_vcpuonline_state *svos = CONTAINER_OF(ev, *svos, timeout);
1926
1927 if (rc == ERROR_TIMEDOUT)
1928 LOGD(ERROR, svos->qmp.domid,
1929 "Setting CPU online in QEMU timed out");
1930
1931 set_vcpuonline_done(egc, svos, rc);
1932 }
1933
set_vcpuonline_done(libxl__egc * egc,set_vcpuonline_state * svos,int rc)1934 static void set_vcpuonline_done(libxl__egc *egc,
1935 set_vcpuonline_state *svos,
1936 int rc)
1937 {
1938 STATE_AO_GC(svos->qmp.ao);
1939
1940 /* Convenience aliases */
1941 libxl_domid domid = svos->qmp.domid;
1942
1943 if (!rc)
1944 rc = libxl__set_vcpuonline_xenstore(gc, domid, svos->cpumap,
1945 &svos->info);
1946
1947 libxl_bitmap_dispose(&svos->final_map);
1948 libxl_dominfo_dispose(&svos->info);
1949 libxl__ev_time_deregister(gc, &svos->timeout);
1950 libxl__ev_qmp_dispose(gc, &svos->qmp);
1951 libxl__ao_complete(egc, ao, rc);
1952 }
1953
1954 static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
1955 const libxl__json_object *response,
1956 int rc);
1957
domain_s3_resume(libxl__ao * ao,libxl__egc * egc,int domid)1958 static void domain_s3_resume(libxl__ao *ao, libxl__egc *egc, int domid)
1959 {
1960 AO_GC;
1961 libxl__ev_qmp *qmp;
1962 int rc = 0;
1963 int r;
1964
1965 GCNEW(qmp);
1966 libxl__ev_qmp_init(qmp);
1967 qmp->ao = ao;
1968 qmp->domid = domid;
1969 qmp->payload_fd = -1;
1970 qmp->callback = domain_s3_resume_done;
1971
1972 switch (libxl__domain_type(gc, domid)) {
1973 case LIBXL_DOMAIN_TYPE_HVM:
1974 switch (libxl__device_model_version_running(gc, domid)) {
1975 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1976 r = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
1977 if (r) {
1978 LOGED(ERROR, domid, "Send trigger '%s' failed",
1979 libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME));
1980 rc = ERROR_FAIL;
1981 }
1982 break;
1983 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1984 rc = libxl__ev_qmp_send(egc, qmp, "system_wakeup", NULL);
1985 if (rc) goto out;
1986 return;
1987 default:
1988 rc = ERROR_INVAL;
1989 break;
1990 }
1991 break;
1992 default:
1993 rc = ERROR_INVAL;
1994 break;
1995 }
1996
1997 out:
1998 domain_s3_resume_done(egc, qmp, NULL, rc);
1999 }
2000
domain_s3_resume_done(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2001 static void domain_s3_resume_done(libxl__egc *egc, libxl__ev_qmp *qmp,
2002 const libxl__json_object *response,
2003 int rc)
2004 {
2005 EGC_GC;
2006
2007 if (rc)
2008 LOGD(ERROR, qmp->domid, "Send trigger '%s' failed, rc=%d",
2009 libxl_trigger_to_string(LIBXL_TRIGGER_S3RESUME), rc);
2010
2011 libxl__ev_qmp_dispose(gc, qmp);
2012 libxl__ao_complete(egc, qmp->ao, rc);
2013 }
2014
libxl_send_trigger(libxl_ctx * ctx,uint32_t domid,libxl_trigger trigger,uint32_t vcpuid,const libxl_asyncop_how * ao_how)2015 int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
2016 libxl_trigger trigger, uint32_t vcpuid,
2017 const libxl_asyncop_how *ao_how)
2018 {
2019 AO_CREATE(ctx, domid, ao_how);
2020 int rc;
2021
2022 switch (trigger) {
2023 case LIBXL_TRIGGER_POWER:
2024 rc = xc_domain_send_trigger(ctx->xch, domid,
2025 XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
2026 break;
2027 case LIBXL_TRIGGER_SLEEP:
2028 rc = xc_domain_send_trigger(ctx->xch, domid,
2029 XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
2030 break;
2031 case LIBXL_TRIGGER_NMI:
2032 rc = xc_domain_send_trigger(ctx->xch, domid,
2033 XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
2034 break;
2035 case LIBXL_TRIGGER_INIT:
2036 rc = xc_domain_send_trigger(ctx->xch, domid,
2037 XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
2038 break;
2039 case LIBXL_TRIGGER_RESET:
2040 rc = xc_domain_send_trigger(ctx->xch, domid,
2041 XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
2042 break;
2043 case LIBXL_TRIGGER_S3RESUME:
2044 domain_s3_resume(ao, egc, domid); /* must be last */
2045 return AO_INPROGRESS;
2046 default:
2047 rc = -1;
2048 errno = EINVAL;
2049 break;
2050 }
2051
2052 if (rc != 0) {
2053 LOGED(ERROR, domid, "Send trigger '%s' failed",
2054 libxl_trigger_to_string(trigger));
2055 rc = ERROR_FAIL;
2056 goto out;
2057 }
2058
2059 libxl__ao_complete(egc, ao, rc);
2060 return AO_INPROGRESS;
2061 out:
2062 return AO_CREATE_FAIL(rc);
2063 }
2064
libxl_vm_get_start_time(libxl_ctx * ctx,uint32_t domid)2065 uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
2066 {
2067 GC_INIT(ctx);
2068 char *dompath = libxl__xs_get_dompath(gc, domid);
2069 char *vm_path, *start_time;
2070 uint32_t ret;
2071
2072 vm_path = libxl__xs_read(
2073 gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
2074 start_time = libxl__xs_read(
2075 gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
2076 if (start_time == NULL) {
2077 LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
2078 ret = -1;
2079 }else{
2080 ret = strtoul(start_time, NULL, 10);
2081 }
2082 GC_FREE;
2083 return ret;
2084 }
2085
libxl__update_avail_vcpus_xenstore(libxl__gc * gc,uint32_t domid,unsigned int max_vcpus,libxl_bitmap * map)2086 static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
2087 unsigned int max_vcpus,
2088 libxl_bitmap *map)
2089 {
2090 int rc;
2091 unsigned int i;
2092 const char *dompath;
2093
2094 dompath = libxl__xs_get_dompath(gc, domid);
2095 if (!dompath) {
2096 rc = ERROR_FAIL;
2097 goto out;
2098 }
2099
2100 for (i = 0; i < max_vcpus; i++) {
2101 const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
2102 const char *content;
2103 rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
2104 if (rc) goto out;
2105 if (content && !strcmp(content, "online"))
2106 libxl_bitmap_set(map, i);
2107 }
2108
2109 rc = 0;
2110 out:
2111 return rc;
2112 }
2113
2114 typedef struct {
2115 libxl__ev_qmp qmp;
2116 libxl__ev_time timeout;
2117 libxl_domain_config *d_config; /* user pointer */
2118 libxl__ev_slowlock devlock;
2119 libxl_bitmap qemuu_cpus;
2120 } retrieve_domain_configuration_state;
2121
2122 static void retrieve_domain_configuration_lock_acquired(
2123 libxl__egc *egc, libxl__ev_slowlock *, int rc);
2124 static void retrieve_domain_configuration_cpu_queried(
2125 libxl__egc *egc, libxl__ev_qmp *qmp,
2126 const libxl__json_object *response, int rc);
2127 static void retrieve_domain_configuration_timeout(libxl__egc *egc,
2128 libxl__ev_time *ev, const struct timeval *requested_abs, int rc);
2129 static void retrieve_domain_configuration_end(libxl__egc *egc,
2130 retrieve_domain_configuration_state *rdcs, int rc);
2131
libxl_retrieve_domain_configuration(libxl_ctx * ctx,uint32_t domid,libxl_domain_config * d_config,const libxl_asyncop_how * ao_how)2132 int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
2133 libxl_domain_config *d_config,
2134 const libxl_asyncop_how *ao_how)
2135 {
2136 AO_CREATE(ctx, domid, ao_how);
2137 retrieve_domain_configuration_state *rdcs;
2138
2139 GCNEW(rdcs);
2140 libxl__ev_qmp_init(&rdcs->qmp);
2141 rdcs->qmp.ao = ao;
2142 rdcs->qmp.domid = domid;
2143 rdcs->qmp.payload_fd = -1;
2144 libxl__ev_time_init(&rdcs->timeout);
2145 rdcs->d_config = d_config;
2146 libxl_bitmap_init(&rdcs->qemuu_cpus);
2147 libxl__ev_devlock_init(&rdcs->devlock);
2148 rdcs->devlock.ao = ao;
2149 rdcs->devlock.domid = domid;
2150 rdcs->devlock.callback = retrieve_domain_configuration_lock_acquired;
2151 libxl__ev_slowlock_lock(egc, &rdcs->devlock);
2152 return AO_INPROGRESS;
2153 }
2154
retrieve_domain_configuration_lock_acquired(libxl__egc * egc,libxl__ev_slowlock * devlock,int rc)2155 static void retrieve_domain_configuration_lock_acquired(
2156 libxl__egc *egc, libxl__ev_slowlock *devlock, int rc)
2157 {
2158 retrieve_domain_configuration_state *rdcs =
2159 CONTAINER_OF(devlock, *rdcs, devlock);
2160 STATE_AO_GC(rdcs->qmp.ao);
2161 libxl__flock *lock = NULL;
2162 bool has_callback = false;
2163
2164 /* Convenience aliases */
2165 libxl_domid domid = rdcs->qmp.domid;
2166 libxl_domain_config *const d_config = rdcs->d_config;
2167
2168 if (rc) goto out;
2169
2170 lock = libxl__lock_domain_userdata(gc, domid);
2171 if (!lock) {
2172 rc = ERROR_LOCK_FAIL;
2173 goto out;
2174 }
2175
2176 rc = libxl__get_domain_configuration(gc, domid, d_config);
2177 if (rc) {
2178 LOGD(ERROR, domid, "Fail to get domain configuration");
2179 rc = ERROR_FAIL;
2180 goto out;
2181 }
2182
2183 libxl__unlock_file(lock);
2184 lock = NULL;
2185
2186 /* We start by querying QEMU, if it is running, for its cpumap as this
2187 * is a long operation. */
2188 if (d_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM &&
2189 libxl__device_model_version_running(gc, domid) ==
2190 LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
2191 /* For QEMU upstream we always need to provide the number
2192 * of cpus present to QEMU whether they are online or not;
2193 * otherwise QEMU won't accept the saved state.
2194 */
2195 rc = libxl__ev_time_register_rel(ao, &rdcs->timeout,
2196 retrieve_domain_configuration_timeout,
2197 LIBXL_QMP_CMD_TIMEOUT * 1000);
2198 if (rc) goto out;
2199 libxl_bitmap_alloc(CTX, &rdcs->qemuu_cpus,
2200 d_config->b_info.max_vcpus);
2201 rdcs->qmp.callback = retrieve_domain_configuration_cpu_queried;
2202 rc = libxl__ev_qmp_send(egc, &rdcs->qmp, "query-cpus", NULL);
2203 if (rc) goto out;
2204 has_callback = true;
2205 }
2206
2207 out:
2208 if (lock) libxl__unlock_file(lock);
2209 if (!has_callback)
2210 retrieve_domain_configuration_end(egc, rdcs, rc);
2211 }
2212
retrieve_domain_configuration_cpu_queried(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)2213 static void retrieve_domain_configuration_cpu_queried(
2214 libxl__egc *egc, libxl__ev_qmp *qmp,
2215 const libxl__json_object *response, int rc)
2216 {
2217 EGC_GC;
2218 retrieve_domain_configuration_state *rdcs =
2219 CONTAINER_OF(qmp, *rdcs, qmp);
2220
2221 if (rc) goto out;
2222
2223 rc = qmp_parse_query_cpus(gc, qmp->domid, response, &rdcs->qemuu_cpus);
2224
2225 out:
2226 retrieve_domain_configuration_end(egc, rdcs, rc);
2227 }
2228
retrieve_domain_configuration_timeout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)2229 static void retrieve_domain_configuration_timeout(libxl__egc *egc,
2230 libxl__ev_time *ev, const struct timeval *requested_abs, int rc)
2231 {
2232 retrieve_domain_configuration_state *rdcs =
2233 CONTAINER_OF(ev, *rdcs, timeout);
2234
2235 retrieve_domain_configuration_end(egc, rdcs, rc);
2236 }
2237
retrieve_domain_configuration_end(libxl__egc * egc,retrieve_domain_configuration_state * rdcs,int rc)2238 static void retrieve_domain_configuration_end(libxl__egc *egc,
2239 retrieve_domain_configuration_state *rdcs, int rc)
2240 {
2241 STATE_AO_GC(rdcs->qmp.ao);
2242 libxl__flock *lock = NULL;
2243
2244 /* Convenience aliases */
2245 libxl_domain_config *const d_config = rdcs->d_config;
2246 libxl_domid domid = rdcs->qmp.domid;
2247
2248 if (rc) goto out;
2249
2250 lock = libxl__lock_domain_userdata(gc, domid);
2251 if (!lock) {
2252 rc = ERROR_LOCK_FAIL;
2253 goto out;
2254 }
2255
2256 /* Domain name */
2257 {
2258 char *domname;
2259 domname = libxl_domid_to_name(CTX, domid);
2260 if (!domname) {
2261 LOGD(ERROR, domid, "Fail to get domain name");
2262 goto out;
2263 }
2264 free(d_config->c_info.name);
2265 d_config->c_info.name = domname; /* steals allocation */
2266 }
2267
2268 /* Domain UUID */
2269 {
2270 libxl_dominfo info;
2271 libxl_dominfo_init(&info);
2272 rc = libxl_domain_info(CTX, &info, domid);
2273 if (rc) {
2274 LOGD(ERROR, domid, "Fail to get domain info");
2275 libxl_dominfo_dispose(&info);
2276 goto out;
2277 }
2278 libxl_uuid_copy(CTX, &d_config->c_info.uuid, &info.uuid);
2279 libxl_dominfo_dispose(&info);
2280 }
2281
2282 /* VCPUs */
2283 {
2284 libxl_bitmap *map = &d_config->b_info.avail_vcpus;
2285 unsigned int max_vcpus = d_config->b_info.max_vcpus;
2286 libxl_device_model_version version;
2287
2288 libxl_bitmap_dispose(map);
2289 libxl_bitmap_init(map);
2290 libxl_bitmap_alloc(CTX, map, max_vcpus);
2291 libxl_bitmap_set_none(map);
2292
2293 switch (d_config->b_info.type) {
2294 case LIBXL_DOMAIN_TYPE_HVM:
2295 version = libxl__device_model_version_running(gc, domid);
2296 assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
2297 switch (version) {
2298 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
2299 libxl_bitmap_copy(CTX, map, &rdcs->qemuu_cpus);
2300 break;
2301 case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
2302 rc = libxl__update_avail_vcpus_xenstore(gc, domid,
2303 max_vcpus, map);
2304 break;
2305 default:
2306 abort();
2307 }
2308 break;
2309 case LIBXL_DOMAIN_TYPE_PVH:
2310 case LIBXL_DOMAIN_TYPE_PV:
2311 rc = libxl__update_avail_vcpus_xenstore(gc, domid,
2312 max_vcpus, map);
2313 break;
2314 default:
2315 abort();
2316 }
2317
2318 if (rc) {
2319 LOGD(ERROR, domid, "Fail to update available cpu map");
2320 goto out;
2321 }
2322 }
2323
2324
2325 /* Memory limits:
2326 *
2327 * Currently there are three memory limits:
2328 * 1. "target" in xenstore (originally memory= in config file)
2329 * 2. "static-max" in xenstore (originally maxmem= in config file)
2330 * 3. "max_memkb" in hypervisor
2331 *
2332 * The third one is not visible and currently managed by
2333 * toolstack. In order to rebuild a domain we only need to have
2334 * "target" and "static-max".
2335 */
2336 {
2337 uint64_t target_memkb = 0, max_memkb = 0;
2338
2339 /* "target" */
2340 rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
2341 if (rc) {
2342 LOGD(ERROR, domid, "Fail to get memory target");
2343 goto out;
2344 }
2345
2346 /* libxl__get_targetmem_fudge() calculates the difference from
2347 * what is in xenstore to what we have in the domain build info.
2348 */
2349 d_config->b_info.target_memkb = target_memkb +
2350 libxl__get_targetmem_fudge(gc, &d_config->b_info);
2351
2352 d_config->b_info.max_memkb = max_memkb;
2353 }
2354
2355 /* Scheduler params */
2356 {
2357 libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
2358 rc = libxl_domain_sched_params_get(CTX, domid,
2359 &d_config->b_info.sched_params);
2360 if (rc) {
2361 LOGD(ERROR, domid, "Fail to get scheduler parameters");
2362 goto out;
2363 }
2364 }
2365
2366 /* Devices: disk, nic, vtpm, pcidev etc. */
2367
2368 /* The MERGE macro implements following logic:
2369 * 0. retrieve JSON (done by now)
2370 * 1. retrieve list of device from xenstore
2371 * 2. use xenstore entries as primary reference and compare JSON
2372 * entries with them.
2373 * a. if a device is present in xenstore and in JSON, merge the
2374 * two views.
2375 * b. if a device is not present in xenstore but in JSON, delete
2376 * it from the result.
2377 * c. it's impossible to have an entry present in xenstore but
2378 * not in JSON, because we maintain an invariant that every
2379 * entry in xenstore must have a corresponding entry in JSON.
2380 * 3. "merge" operates on "src" and "dst". "src" points to the
2381 * entry retrieved from xenstore while "dst" points to the entry
2382 * retrieve from JSON.
2383 */
2384 {
2385 const libxl__device_type *dt;
2386 int idx;
2387
2388 for (idx = 0;; idx++) {
2389 void *p = NULL;
2390 void **devs;
2391 int i, j, num;
2392 int *num_dev;
2393
2394 dt = device_type_tbl[idx];
2395 if (!dt)
2396 break;
2397
2398 if (!dt->compare)
2399 continue;
2400
2401 num_dev = libxl__device_type_get_num(dt, d_config);
2402 p = libxl__device_list(gc, dt, domid, &num);
2403 if (p == NULL) {
2404 LOGD(DEBUG, domid, "No %s from xenstore",
2405 libxl__device_kind_to_string(dt->type));
2406 }
2407 devs = libxl__device_type_get_ptr(dt, d_config);
2408
2409 for (i = 0; i < *num_dev; i++) {
2410 void *q;
2411
2412 q = libxl__device_type_get_elem(dt, d_config, i);
2413 for (j = 0; j < num; j++) {
2414 if (dt->compare(p + dt->dev_elem_size * j, q))
2415 break;
2416 }
2417
2418 if (j < num) { /* found in xenstore */
2419 if (dt->merge)
2420 dt->merge(CTX, p + dt->dev_elem_size * j, q);
2421 } else { /* not found in xenstore */
2422 LOGD(WARN, domid,
2423 "Device present in JSON but not in xenstore, ignored");
2424
2425 dt->dispose(q);
2426
2427 for (j = i; j < *num_dev - 1; j++)
2428 memcpy(libxl__device_type_get_elem(dt, d_config, j),
2429 libxl__device_type_get_elem(dt, d_config, j+1),
2430 dt->dev_elem_size);
2431
2432 /* rewind counters */
2433 (*num_dev)--;
2434 i--;
2435
2436 *devs = libxl__realloc(NOGC, *devs,
2437 dt->dev_elem_size * *num_dev);
2438 }
2439 }
2440
2441 for (i = 0; i < num; i++)
2442 dt->dispose(p + dt->dev_elem_size * i);
2443 free(p);
2444 }
2445 }
2446
2447 out:
2448 libxl__ev_slowlock_unlock(gc, &rdcs->devlock);
2449 if (lock) libxl__unlock_file(lock);
2450 libxl_bitmap_dispose(&rdcs->qemuu_cpus);
2451 libxl__ev_qmp_dispose(gc, &rdcs->qmp);
2452 libxl__ev_time_deregister(gc, &rdcs->timeout);
2453 libxl__ao_complete(egc, ao, rc);
2454 }
2455
2456 /*
2457 * Local variables:
2458 * mode: C
2459 * c-basic-offset: 4
2460 * indent-tabs-mode: nil
2461 * End:
2462 */
2463