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(&current_map);
1875 
1876     if (rc) goto out;
1877 
1878     libxl_bitmap_alloc(CTX, &current_map, svos->info.vcpu_max_id + 1);
1879     rc = qmp_parse_query_cpus(gc, qmp->domid, response, &current_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(&current_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