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 
libxl_domain_resume(libxl_ctx * ctx,uint32_t domid,int suspend_cancel,const libxl_asyncop_how * ao_how)177 int libxl_domain_resume(libxl_ctx *ctx, uint32_t domid, int suspend_cancel,
178                         const libxl_asyncop_how *ao_how)
179 {
180     AO_CREATE(ctx, domid, ao_how);
181     int rc = libxl__domain_resume(gc, domid, suspend_cancel);
182     libxl__ao_complete(egc, ao, rc);
183     return AO_INPROGRESS;
184 }
185 
186 /*
187  * Preserves a domain but rewrites xenstore etc to make it unique so
188  * that the domain can be restarted.
189  *
190  * Does not modify info so that it may be reused.
191  */
libxl_domain_preserve(libxl_ctx * ctx,uint32_t domid,libxl_domain_create_info * info,const char * name_suffix,libxl_uuid new_uuid)192 int libxl_domain_preserve(libxl_ctx *ctx, uint32_t domid,
193                           libxl_domain_create_info *info, const char *name_suffix, libxl_uuid new_uuid)
194 {
195     GC_INIT(ctx);
196     struct xs_permissions roperm[2];
197     xs_transaction_t t;
198     char *preserved_name;
199     char *uuid_string;
200     char *vm_path;
201     char *dom_path;
202 
203     int rc;
204 
205     preserved_name = GCSPRINTF("%s%s", info->name, name_suffix);
206     if (!preserved_name) {
207         GC_FREE;
208         return ERROR_NOMEM;
209     }
210 
211     uuid_string = libxl__uuid2string(gc, new_uuid);
212     if (!uuid_string) {
213         GC_FREE;
214         return ERROR_NOMEM;
215     }
216 
217     dom_path = libxl__xs_get_dompath(gc, domid);
218     if (!dom_path) {
219         GC_FREE;
220         return ERROR_FAIL;
221     }
222 
223     vm_path = GCSPRINTF("/vm/%s", uuid_string);
224     if (!vm_path) {
225         GC_FREE;
226         return ERROR_FAIL;
227     }
228 
229     roperm[0].id = 0;
230     roperm[0].perms = XS_PERM_NONE;
231     roperm[1].id = domid;
232     roperm[1].perms = XS_PERM_READ;
233 
234  retry_transaction:
235     t = xs_transaction_start(ctx->xsh);
236 
237     xs_rm(ctx->xsh, t, vm_path);
238     xs_mkdir(ctx->xsh, t, vm_path);
239     xs_set_permissions(ctx->xsh, t, vm_path, roperm, ARRAY_SIZE(roperm));
240 
241     xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
242     rc = libxl__domain_rename(gc, domid, info->name, preserved_name, t);
243     if (rc) {
244         GC_FREE;
245         return rc;
246     }
247 
248     xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
249 
250     if (!xs_transaction_end(ctx->xsh, t, 0))
251         if (errno == EAGAIN)
252             goto retry_transaction;
253 
254     GC_FREE;
255     return 0;
256 }
257 
libxl__xcinfo2xlinfo(libxl_ctx * ctx,const xc_domaininfo_t * xcinfo,libxl_dominfo * xlinfo)258 void libxl__xcinfo2xlinfo(libxl_ctx *ctx,
259                           const xc_domaininfo_t *xcinfo,
260                           libxl_dominfo *xlinfo)
261 {
262     size_t size;
263 
264     memcpy(&(xlinfo->uuid), xcinfo->handle, sizeof(xen_domain_handle_t));
265     xlinfo->domid = xcinfo->domain;
266     xlinfo->ssidref = xcinfo->ssidref;
267     if (libxl_flask_sid_to_context(ctx, xlinfo->ssidref,
268                                    &xlinfo->ssid_label, &size) < 0)
269         xlinfo->ssid_label = NULL;
270 
271     xlinfo->dying      = !!(xcinfo->flags&XEN_DOMINF_dying);
272     xlinfo->shutdown   = !!(xcinfo->flags&XEN_DOMINF_shutdown);
273     xlinfo->paused     = !!(xcinfo->flags&XEN_DOMINF_paused);
274     xlinfo->blocked    = !!(xcinfo->flags&XEN_DOMINF_blocked);
275     xlinfo->running    = !!(xcinfo->flags&XEN_DOMINF_running);
276     xlinfo->never_stop = !!(xcinfo->flags&XEN_DOMINF_xs_domain);
277 
278     if (xlinfo->shutdown)
279         xlinfo->shutdown_reason = (xcinfo->flags>>XEN_DOMINF_shutdownshift) & XEN_DOMINF_shutdownmask;
280     else
281         xlinfo->shutdown_reason = LIBXL_SHUTDOWN_REASON_UNKNOWN;
282 
283     xlinfo->outstanding_memkb = PAGE_TO_MEMKB(xcinfo->outstanding_pages);
284     xlinfo->current_memkb = PAGE_TO_MEMKB(xcinfo->tot_pages);
285     xlinfo->shared_memkb = PAGE_TO_MEMKB(xcinfo->shr_pages);
286     xlinfo->paged_memkb = PAGE_TO_MEMKB(xcinfo->paged_pages);
287     xlinfo->max_memkb = PAGE_TO_MEMKB(xcinfo->max_pages);
288     xlinfo->cpu_time = xcinfo->cpu_time;
289     xlinfo->vcpu_max_id = xcinfo->max_vcpu_id;
290     xlinfo->vcpu_online = xcinfo->nr_online_vcpus;
291     xlinfo->cpupool = xcinfo->cpupool;
292     xlinfo->domain_type = (xcinfo->flags & XEN_DOMINF_hvm_guest) ?
293         LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PV;
294 }
295 
libxl_list_domain(libxl_ctx * ctx,int * nb_domain_out)296 libxl_dominfo * libxl_list_domain(libxl_ctx *ctx, int *nb_domain_out)
297 {
298     libxl_dominfo *ptr = NULL;
299     int i, ret;
300     xc_domaininfo_t info[1024];
301     int size = 0;
302     uint32_t domid = 0;
303     GC_INIT(ctx);
304 
305     while ((ret = xc_domain_getinfolist(ctx->xch, domid, 1024, info)) > 0) {
306         ptr = libxl__realloc(NOGC, ptr, (size + ret) * sizeof(libxl_dominfo));
307         for (i = 0; i < ret; i++) {
308             libxl__xcinfo2xlinfo(ctx, &info[i], &ptr[size + i]);
309         }
310         domid = info[ret - 1].domain + 1;
311         size += ret;
312     }
313 
314     if (ret < 0) {
315         LOGE(ERROR, "getting domain info list");
316         free(ptr);
317         GC_FREE;
318         return NULL;
319     }
320 
321     *nb_domain_out = size;
322     GC_FREE;
323     return ptr;
324 }
325 
libxl_domain_info(libxl_ctx * ctx,libxl_dominfo * info_r,uint32_t domid)326 int libxl_domain_info(libxl_ctx *ctx, libxl_dominfo *info_r,
327                       uint32_t domid) {
328     xc_domaininfo_t xcinfo;
329     int ret;
330     GC_INIT(ctx);
331 
332     ret = xc_domain_getinfolist(ctx->xch, domid, 1, &xcinfo);
333     if (ret<0) {
334         LOGED(ERROR, domid, "Getting domain info list");
335         GC_FREE;
336         return ERROR_FAIL;
337     }
338     if (ret==0 || xcinfo.domain != domid) {
339         GC_FREE;
340         return ERROR_DOMAIN_NOTFOUND;
341     }
342 
343     if (info_r)
344         libxl__xcinfo2xlinfo(ctx, &xcinfo, info_r);
345     GC_FREE;
346     return 0;
347 }
348 
349 /* this API call only list VM running on this host. A VM can
350  * be an aggregate of multiple domains. */
libxl_list_vm(libxl_ctx * ctx,int * nb_vm_out)351 libxl_vminfo * libxl_list_vm(libxl_ctx *ctx, int *nb_vm_out)
352 {
353     GC_INIT(ctx);
354     libxl_dominfo *info;
355     libxl_vminfo *ptr = NULL;
356     int idx, i, n_doms;
357 
358     info = libxl_list_domain(ctx, &n_doms);
359     if (!info)
360         goto out;
361 
362     /*
363      * Always make sure to allocate at least one element; if we don't and we
364      * request zero, libxl__calloc (might) think its internal call to calloc
365      * has failed (if it returns null), if so it would kill our process.
366      */
367     ptr = libxl__calloc(NOGC, n_doms ? n_doms : 1, sizeof(libxl_vminfo));
368 
369     for (idx = i = 0; i < n_doms; i++) {
370         if (libxl_is_stubdom(ctx, info[i].domid, NULL))
371             continue;
372         ptr[idx].uuid = info[i].uuid;
373         ptr[idx].domid = info[i].domid;
374 
375         idx++;
376     }
377     *nb_vm_out = idx;
378     libxl_dominfo_list_free(info, n_doms);
379 
380 out:
381     GC_FREE;
382     return ptr;
383 }
384 
385 static void remus_failover_cb(libxl__egc *egc,
386                               libxl__domain_save_state *dss, int rc);
387 
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)388 int libxl_domain_remus_start(libxl_ctx *ctx, libxl_domain_remus_info *info,
389                              uint32_t domid, int send_fd, int recv_fd,
390                              const libxl_asyncop_how *ao_how)
391 {
392     AO_CREATE(ctx, domid, ao_how);
393     libxl__domain_save_state *dss;
394     int rc;
395 
396     libxl_domain_type type = libxl__domain_type(gc, domid);
397     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
398         rc = ERROR_FAIL;
399         goto out;
400     }
401 
402     /* The caller must set this defbool */
403     if (libxl_defbool_is_default(info->colo)) {
404         LOGD(ERROR, domid, "Colo mode must be enabled/disabled");
405         rc = ERROR_FAIL;
406         goto out;
407     }
408 
409     libxl_defbool_setdefault(&info->allow_unsafe, false);
410     libxl_defbool_setdefault(&info->blackhole, false);
411     libxl_defbool_setdefault(&info->compression,
412                              !libxl_defbool_val(info->colo));
413     libxl_defbool_setdefault(&info->netbuf, true);
414     libxl_defbool_setdefault(&info->diskbuf, true);
415 
416     if (libxl_defbool_val(info->colo) &&
417         libxl_defbool_val(info->compression)) {
418             LOGD(ERROR, domid, "Cannot use memory checkpoint "
419                         "compression in COLO mode");
420             rc = ERROR_FAIL;
421             goto out;
422     }
423 
424     if (!libxl_defbool_val(info->allow_unsafe) &&
425         (libxl_defbool_val(info->blackhole) ||
426          !libxl_defbool_val(info->netbuf) ||
427          !libxl_defbool_val(info->diskbuf))) {
428         LOGD(ERROR, domid, "Unsafe mode must be enabled to replicate to /dev/null,"
429                     "disable network buffering and disk replication");
430         rc = ERROR_FAIL;
431         goto out;
432     }
433 
434 
435     GCNEW(dss);
436     dss->ao = ao;
437     dss->callback = remus_failover_cb;
438     dss->domid = domid;
439     dss->fd = send_fd;
440     dss->recv_fd = recv_fd;
441     dss->type = type;
442     dss->live = 1;
443     dss->debug = 0;
444     dss->remus = info;
445     if (libxl_defbool_val(info->colo))
446         dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_COLO;
447     else
448         dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_REMUS;
449 
450     assert(info);
451 
452     /* Point of no return */
453     if (libxl_defbool_val(info->colo))
454         libxl__colo_save_setup(egc, &dss->css);
455     else
456         libxl__remus_setup(egc, &dss->rs);
457     return AO_INPROGRESS;
458 
459  out:
460     return AO_CREATE_FAIL(rc);
461 }
462 
remus_failover_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)463 static void remus_failover_cb(libxl__egc *egc,
464                               libxl__domain_save_state *dss, int rc)
465 {
466     STATE_AO_GC(dss->ao);
467     /*
468      * With Remus, if we reach this point, it means either
469      * backup died or some network error occurred preventing us
470      * from sending checkpoints.
471      */
472     libxl__ao_complete(egc, ao, rc);
473 }
474 
domain_suspend_cb(libxl__egc * egc,libxl__domain_save_state * dss,int rc)475 static void domain_suspend_cb(libxl__egc *egc,
476                               libxl__domain_save_state *dss, int rc)
477 {
478     STATE_AO_GC(dss->ao);
479     int flrc;
480 
481     flrc = libxl__fd_flags_restore(gc, dss->fd, dss->fdfl);
482     /* If suspend has failed already then report that error not this one. */
483     if (flrc && !rc) rc = flrc;
484 
485     libxl__ao_complete(egc,ao,rc);
486 
487 }
488 
libxl_domain_suspend(libxl_ctx * ctx,uint32_t domid,int fd,int flags,const libxl_asyncop_how * ao_how)489 int libxl_domain_suspend(libxl_ctx *ctx, uint32_t domid, int fd, int flags,
490                          const libxl_asyncop_how *ao_how)
491 {
492     AO_CREATE(ctx, domid, ao_how);
493     int rc;
494 
495     libxl_domain_type type = libxl__domain_type(gc, domid);
496     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
497         rc = ERROR_FAIL;
498         goto out_err;
499     }
500 
501     libxl__domain_save_state *dss;
502     GCNEW(dss);
503 
504     dss->ao = ao;
505     dss->callback = domain_suspend_cb;
506 
507     dss->domid = domid;
508     dss->fd = fd;
509     dss->type = type;
510     dss->live = flags & LIBXL_SUSPEND_LIVE;
511     dss->debug = flags & LIBXL_SUSPEND_DEBUG;
512     dss->checkpointed_stream = LIBXL_CHECKPOINTED_STREAM_NONE;
513 
514     rc = libxl__fd_flags_modify_save(gc, dss->fd,
515                                      ~(O_NONBLOCK|O_NDELAY), 0,
516                                      &dss->fdfl);
517     if (rc < 0) goto out_err;
518 
519     libxl__domain_save(egc, dss);
520     return AO_INPROGRESS;
521 
522  out_err:
523     return AO_CREATE_FAIL(rc);
524 }
525 
libxl_domain_pause(libxl_ctx * ctx,uint32_t domid)526 int libxl_domain_pause(libxl_ctx *ctx, uint32_t domid)
527 {
528     int ret;
529     GC_INIT(ctx);
530     ret = xc_domain_pause(ctx->xch, domid);
531     if (ret<0) {
532         LOGED(ERROR, domid, "Pausing domain");
533         GC_FREE;
534         return ERROR_FAIL;
535     }
536     GC_FREE;
537     return 0;
538 }
539 
libxl_domain_core_dump(libxl_ctx * ctx,uint32_t domid,const char * filename,const libxl_asyncop_how * ao_how)540 int libxl_domain_core_dump(libxl_ctx *ctx, uint32_t domid,
541                            const char *filename,
542                            const libxl_asyncop_how *ao_how)
543 {
544     AO_CREATE(ctx, domid, ao_how);
545     int ret, rc;
546 
547     ret = xc_domain_dumpcore(ctx->xch, domid, filename);
548     if (ret<0) {
549         LOGED(ERROR, domid, "Core dumping domain to %s", filename);
550         rc = ERROR_FAIL;
551         goto out;
552     }
553 
554     rc = 0;
555 out:
556 
557     libxl__ao_complete(egc, ao, rc);
558 
559     return AO_INPROGRESS;
560 }
561 
libxl_domain_unpause(libxl_ctx * ctx,uint32_t domid)562 int libxl_domain_unpause(libxl_ctx *ctx, uint32_t domid)
563 {
564     GC_INIT(ctx);
565     int ret, rc = 0;
566 
567     libxl_domain_type type = libxl__domain_type(gc, domid);
568     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
569         rc = ERROR_FAIL;
570         goto out;
571     }
572 
573     if (type == LIBXL_DOMAIN_TYPE_HVM) {
574         rc = libxl__domain_resume_device_model(gc, domid);
575         if (rc < 0) {
576             LOGD(ERROR, domid,
577                  "Failed to unpause device model for domain: %d", rc);
578             goto out;
579         }
580     }
581     ret = xc_domain_unpause(ctx->xch, domid);
582     if (ret<0) {
583         LOGED(ERROR, domid, "Unpausing domain");
584         rc = ERROR_FAIL;
585     }
586  out:
587     GC_FREE;
588     return rc;
589 }
590 
libxl__domain_pvcontrol_available(libxl__gc * gc,uint32_t domid)591 int libxl__domain_pvcontrol_available(libxl__gc *gc, uint32_t domid)
592 {
593     libxl_ctx *ctx = libxl__gc_owner(gc);
594 
595     uint64_t pvdriver = 0;
596     int ret;
597 
598     libxl_domain_type domtype = libxl__domain_type(gc, domid);
599     if (domtype == LIBXL_DOMAIN_TYPE_INVALID)
600         return ERROR_FAIL;
601 
602     if (domtype == LIBXL_DOMAIN_TYPE_PV)
603         return 1;
604 
605     ret = xc_hvm_param_get(ctx->xch, domid, HVM_PARAM_CALLBACK_IRQ, &pvdriver);
606     if (ret<0) {
607         LOGED(ERROR, domid, "Getting HVM callback IRQ");
608         return ERROR_FAIL;
609     }
610     return !!pvdriver;
611 }
612 
libxl__domain_pvcontrol_xspath(libxl__gc * gc,uint32_t domid)613 const char *libxl__domain_pvcontrol_xspath(libxl__gc *gc, uint32_t domid)
614 {
615     const char *dom_path;
616 
617     dom_path = libxl__xs_get_dompath(gc, domid);
618     if (!dom_path)
619         return NULL;
620 
621     return GCSPRINTF("%s/control/shutdown", dom_path);
622 }
623 
libxl__domain_pvcontrol_read(libxl__gc * gc,xs_transaction_t t,uint32_t domid)624 char * libxl__domain_pvcontrol_read(libxl__gc *gc, xs_transaction_t t,
625                                     uint32_t domid)
626 {
627     const char *shutdown_path;
628 
629     shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
630     if (!shutdown_path)
631         return NULL;
632 
633     return libxl__xs_read(gc, t, shutdown_path);
634 }
635 
libxl__domain_pvcontrol_write(libxl__gc * gc,xs_transaction_t t,uint32_t domid,const char * cmd)636 int libxl__domain_pvcontrol_write(libxl__gc *gc, xs_transaction_t t,
637                                   uint32_t domid, const char *cmd)
638 {
639     const char *shutdown_path;
640 
641     shutdown_path = libxl__domain_pvcontrol_xspath(gc, domid);
642     if (!shutdown_path)
643         return ERROR_FAIL;
644 
645     return libxl__xs_printf(gc, t, shutdown_path, "%s", cmd);
646 }
647 
libxl__domain_pvcontrol(libxl__gc * gc,uint32_t domid,const char * cmd)648 static int libxl__domain_pvcontrol(libxl__gc *gc, uint32_t domid,
649                                    const char *cmd)
650 {
651     int ret;
652 
653     ret = libxl__domain_pvcontrol_available(gc, domid);
654     if (ret < 0)
655         return ret;
656 
657     if (!ret)
658         return ERROR_NOPARAVIRT;
659 
660     return libxl__domain_pvcontrol_write(gc, XBT_NULL, domid, cmd);
661 }
662 
libxl_domain_shutdown(libxl_ctx * ctx,uint32_t domid)663 int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t domid)
664 {
665     GC_INIT(ctx);
666     int ret;
667     ret = libxl__domain_pvcontrol(gc, domid, "poweroff");
668     GC_FREE;
669     return ret;
670 }
671 
libxl_domain_reboot(libxl_ctx * ctx,uint32_t domid)672 int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid)
673 {
674     GC_INIT(ctx);
675     int ret;
676     ret = libxl__domain_pvcontrol(gc, domid, "reboot");
677     GC_FREE;
678     return ret;
679 }
680 
domain_death_occurred(libxl__egc * egc,libxl_evgen_domain_death ** evg_upd,const char * why)681 static void domain_death_occurred(libxl__egc *egc,
682                                   libxl_evgen_domain_death **evg_upd,
683                                   const char *why) {
684     /* Removes **evg_upd from death_list and puts it on death_reported
685      * and advances *evg_upd to the next entry.
686      * Call sites in domain_death_xswatch_callback must use "continue". */
687     EGC_GC;
688     libxl_evgen_domain_death *const evg = *evg_upd;
689 
690     LOGD(DEBUG, evg->domid, "%s", why);
691 
692     libxl_evgen_domain_death *evg_next = LIBXL_TAILQ_NEXT(evg, entry);
693     *evg_upd = evg_next;
694 
695     libxl_event *ev = NEW_EVENT(egc, DOMAIN_DEATH, evg->domid, evg->user);
696 
697     libxl__event_occurred(egc, ev);
698 
699     evg->death_reported = 1;
700     LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
701     LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
702 }
703 
domain_death_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * w,const char * wpath,const char * epath)704 static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
705                                         const char *wpath, const char *epath) {
706     EGC_GC;
707     libxl_evgen_domain_death *evg;
708     int rc;
709 
710     CTX_LOCK;
711 
712     evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
713 
714     for (;;) {
715         if (!evg) goto out;
716 
717         int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
718         xc_domaininfo_t domaininfos[nentries];
719         const xc_domaininfo_t *got = domaininfos, *gotend;
720 
721         rc = xc_domain_getinfolist(CTX->xch, evg->domid, nentries, domaininfos);
722         if (rc == -1) {
723             LIBXL__EVENT_DISASTER(egc, "xc_domain_getinfolist failed while"
724                                   " processing @releaseDomain watch event",
725                                   errno, 0);
726             goto out;
727         }
728         gotend = &domaininfos[rc];
729 
730         LOGD(DEBUG, evg->domid, "[evg=%p] nentries=%d rc=%d %ld..%ld",
731              evg, nentries, rc,
732              rc>0 ? (long)domaininfos[0].domain : 0,
733              rc>0 ? (long)domaininfos[rc-1].domain : 0);
734 
735         for (;;) {
736             if (!evg) {
737                 LOG(DEBUG, "[evg=0] all reported");
738                 goto all_reported;
739             }
740 
741             LOGD(DEBUG, evg->domid, "[evg=%p]"
742                  "   got=domaininfos[%d] got->domain=%ld",
743                  evg, (int)(got - domaininfos),
744                  got < gotend ? (long)got->domain : -1L);
745 
746             if (!rc) {
747                 domain_death_occurred(egc, &evg, "empty list");
748                 continue;
749             }
750 
751             if (got == gotend) {
752                 LOG(DEBUG, " got==gotend");
753                 break;
754             }
755 
756             if (got->domain > evg->domid) {
757                 /* ie, the list doesn't contain evg->domid any more so
758                  * the domain has been destroyed */
759                 domain_death_occurred(egc, &evg, "missing from list");
760                 continue;
761             }
762 
763             if (got->domain < evg->domid) {
764                 got++;
765                 continue;
766             }
767 
768             assert(evg->domid == got->domain);
769             LOGD(DEBUG, evg->domid, "Exists shutdown_reported=%d"" dominf.flags=%x",
770                  evg->shutdown_reported, got->flags);
771 
772             if (got->flags & XEN_DOMINF_dying) {
773                 domain_death_occurred(egc, &evg, "dying");
774                 continue;
775             }
776 
777             if (!evg->shutdown_reported &&
778                 (got->flags & XEN_DOMINF_shutdown)) {
779                 libxl_event *ev = NEW_EVENT(egc, DOMAIN_SHUTDOWN,
780                                             got->domain, evg->user);
781 
782                 LOG(DEBUG, " shutdown reporting");
783 
784                 ev->u.domain_shutdown.shutdown_reason =
785                     (got->flags >> XEN_DOMINF_shutdownshift) &
786                     XEN_DOMINF_shutdownmask;
787                 libxl__event_occurred(egc, ev);
788 
789                 evg->shutdown_reported = 1;
790             }
791             evg = LIBXL_TAILQ_NEXT(evg, entry);
792         }
793 
794         assert(rc); /* rc==0 results in us eating all evgs and quitting */
795     }
796  all_reported:
797  out:
798 
799     LOG(DEBUG, "domain death search done");
800 
801     CTX_UNLOCK;
802 }
803 
libxl_evenable_domain_death(libxl_ctx * ctx,uint32_t domid,libxl_ev_user user,libxl_evgen_domain_death ** evgen_out)804 int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
805                 libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
806     GC_INIT(ctx);
807     libxl_evgen_domain_death *evg, *evg_search;
808     int rc;
809 
810     CTX_LOCK;
811 
812     evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
813     memset(evg, 0, sizeof(*evg));
814     evg->domid = domid;
815     evg->user = user;
816 
817     LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
818                               evg->domid > evg_search->domid);
819 
820     if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
821         rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
822                         domain_death_xswatch_callback, "@releaseDomain");
823         if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
824     }
825 
826     *evgen_out = evg;
827     rc = 0;
828 
829  out:
830     CTX_UNLOCK;
831     GC_FREE;
832     return rc;
833 };
834 
libxl__evdisable_domain_death(libxl__gc * gc,libxl_evgen_domain_death * evg)835 void libxl__evdisable_domain_death(libxl__gc *gc,
836                                    libxl_evgen_domain_death *evg) {
837     CTX_LOCK;
838 
839     if (!evg->death_reported)
840         LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
841     else
842         LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
843 
844     free(evg);
845 
846     if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
847         libxl__ev_xswatch_isregistered(&CTX->death_watch))
848         libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
849 
850     CTX_UNLOCK;
851 }
852 
libxl_evdisable_domain_death(libxl_ctx * ctx,libxl_evgen_domain_death * evg)853 void libxl_evdisable_domain_death(libxl_ctx *ctx,
854                                   libxl_evgen_domain_death *evg) {
855     GC_INIT(ctx);
856     libxl__evdisable_domain_death(gc, evg);
857     GC_FREE;
858 }
859 
860 /* Callbacks for libxl_domain_destroy */
861 
862 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
863                               int rc);
864 
libxl_domain_destroy(libxl_ctx * ctx,uint32_t domid,const libxl_asyncop_how * ao_how)865 int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid,
866                          const libxl_asyncop_how *ao_how)
867 {
868     AO_CREATE(ctx, domid, ao_how);
869     libxl__domain_destroy_state *dds;
870 
871     GCNEW(dds);
872     dds->ao = ao;
873     dds->domid = domid;
874     dds->callback = domain_destroy_cb;
875     libxl__domain_destroy(egc, dds);
876 
877     return AO_INPROGRESS;
878 }
879 
domain_destroy_cb(libxl__egc * egc,libxl__domain_destroy_state * dds,int rc)880 static void domain_destroy_cb(libxl__egc *egc, libxl__domain_destroy_state *dds,
881                               int rc)
882 {
883     STATE_AO_GC(dds->ao);
884 
885     if (rc)
886         LOGD(ERROR, dds->domid, "Destruction of domain failed");
887 
888     libxl__ao_complete(egc, ao, rc);
889 }
890 
891 /* Callbacks for libxl__domain_destroy */
892 
893 static void stubdom_destroy_callback(libxl__egc *egc,
894                                      libxl__destroy_domid_state *dis,
895                                      int rc);
896 
897 static void domain_destroy_callback(libxl__egc *egc,
898                                     libxl__destroy_domid_state *dis,
899                                     int rc);
900 
901 static void destroy_finish_check(libxl__egc *egc,
902                                  libxl__domain_destroy_state *dds);
903 
libxl__domain_destroy(libxl__egc * egc,libxl__domain_destroy_state * dds)904 void libxl__domain_destroy(libxl__egc *egc, libxl__domain_destroy_state *dds)
905 {
906     STATE_AO_GC(dds->ao);
907     uint32_t stubdomid = libxl_get_stubdom_id(CTX, dds->domid);
908 
909     if (stubdomid) {
910         dds->stubdom.ao = ao;
911         dds->stubdom.domid = stubdomid;
912         dds->stubdom.callback = stubdom_destroy_callback;
913         dds->stubdom.soft_reset = false;
914         libxl__destroy_domid(egc, &dds->stubdom);
915     } else {
916         dds->stubdom_finished = 1;
917     }
918 
919     dds->domain.ao = ao;
920     dds->domain.domid = dds->domid;
921     dds->domain.callback = domain_destroy_callback;
922     dds->domain.soft_reset = dds->soft_reset;
923     libxl__destroy_domid(egc, &dds->domain);
924 }
925 
stubdom_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)926 static void stubdom_destroy_callback(libxl__egc *egc,
927                                      libxl__destroy_domid_state *dis,
928                                      int rc)
929 {
930     STATE_AO_GC(dis->ao);
931     libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, stubdom);
932     const char *savefile;
933 
934     if (rc) {
935         LOGD(ERROR, dds->domain.domid, "Unable to destroy stubdom with domid %u",
936              dis->domid);
937         dds->rc = rc;
938     }
939 
940     dds->stubdom_finished = 1;
941     savefile = libxl__device_model_savefile(gc, dis->domid);
942     rc = libxl__remove_file(gc, savefile);
943     if (rc) {
944         LOGD(ERROR, dds->domain.domid, "Failed to remove device-model savefile %s",
945              savefile);
946     }
947 
948     destroy_finish_check(egc, dds);
949 }
950 
domain_destroy_callback(libxl__egc * egc,libxl__destroy_domid_state * dis,int rc)951 static void domain_destroy_callback(libxl__egc *egc,
952                                     libxl__destroy_domid_state *dis,
953                                     int rc)
954 {
955     STATE_AO_GC(dis->ao);
956     libxl__domain_destroy_state *dds = CONTAINER_OF(dis, *dds, domain);
957 
958     if (rc) {
959         LOGD(ERROR, dis->domid, "Unable to destroy guest");
960         dds->rc = rc;
961     }
962 
963     dds->domain_finished = 1;
964     destroy_finish_check(egc, dds);
965 }
966 
destroy_finish_check(libxl__egc * egc,libxl__domain_destroy_state * dds)967 static void destroy_finish_check(libxl__egc *egc,
968                                  libxl__domain_destroy_state *dds)
969 {
970     if (!(dds->domain_finished && dds->stubdom_finished))
971         return;
972 
973     dds->callback(egc, dds, dds->rc);
974 }
975 
976 /* Callbacks for libxl__destroy_domid */
977 static void devices_destroy_cb(libxl__egc *egc,
978                                libxl__devices_remove_state *drs,
979                                int rc);
980 
981 static void domain_destroy_domid_cb(libxl__egc *egc,
982                                     libxl__ev_child *destroyer,
983                                     pid_t pid, int status);
984 
libxl__destroy_domid(libxl__egc * egc,libxl__destroy_domid_state * dis)985 void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
986 {
987     STATE_AO_GC(dis->ao);
988     libxl_ctx *ctx = CTX;
989     uint32_t domid = dis->domid;
990     char *dom_path;
991     int rc, dm_present;
992 
993     libxl__ev_child_init(&dis->destroyer);
994 
995     rc = libxl_domain_info(ctx, NULL, domid);
996     switch(rc) {
997     case 0:
998         break;
999     case ERROR_DOMAIN_NOTFOUND:
1000         LOGD(ERROR, domid, "Non-existant domain");
1001     default:
1002         goto out;
1003     }
1004 
1005     switch (libxl__domain_type(gc, domid)) {
1006     case LIBXL_DOMAIN_TYPE_HVM:
1007         if (libxl_get_stubdom_id(CTX, domid)) {
1008             dm_present = 0;
1009             break;
1010         }
1011         /* fall through */
1012     case LIBXL_DOMAIN_TYPE_PVH:
1013     case LIBXL_DOMAIN_TYPE_PV:
1014         dm_present = libxl__dm_active(gc, domid);
1015         break;
1016     case LIBXL_DOMAIN_TYPE_INVALID:
1017         rc = ERROR_FAIL;
1018         goto out;
1019     default:
1020         abort();
1021     }
1022 
1023     dom_path = libxl__xs_get_dompath(gc, domid);
1024     if (!dom_path) {
1025         rc = ERROR_FAIL;
1026         goto out;
1027     }
1028 
1029     if (libxl__device_pci_destroy_all(gc, domid) < 0)
1030         LOGD(ERROR, domid, "Pci shutdown failed");
1031     rc = xc_domain_pause(ctx->xch, domid);
1032     if (rc < 0) {
1033         LOGEVD(ERROR, rc, domid, "xc_domain_pause failed");
1034     }
1035     if (dm_present) {
1036         if (libxl__destroy_device_model(gc, domid) < 0)
1037             LOGD(ERROR, domid, "libxl__destroy_device_model failed");
1038 
1039         libxl__qmp_cleanup(gc, domid);
1040     }
1041     dis->drs.ao = ao;
1042     dis->drs.domid = domid;
1043     dis->drs.callback = devices_destroy_cb;
1044     dis->drs.force = 1;
1045     libxl__devices_destroy(egc, &dis->drs);
1046     return;
1047 
1048 out:
1049     assert(rc);
1050     dis->callback(egc, dis, rc);
1051     return;
1052 }
1053 
devices_destroy_cb(libxl__egc * egc,libxl__devices_remove_state * drs,int rc)1054 static void devices_destroy_cb(libxl__egc *egc,
1055                                libxl__devices_remove_state *drs,
1056                                int rc)
1057 {
1058     STATE_AO_GC(drs->ao);
1059     libxl__destroy_domid_state *dis = CONTAINER_OF(drs, *dis, drs);
1060     libxl_ctx *ctx = CTX;
1061     uint32_t domid = dis->domid;
1062     char *dom_path;
1063     char *vm_path;
1064     libxl__domain_userdata_lock *lock;
1065 
1066     dom_path = libxl__xs_get_dompath(gc, domid);
1067     if (!dom_path) {
1068         rc = ERROR_FAIL;
1069         goto out;
1070     }
1071 
1072     if (rc < 0)
1073         LOGD(ERROR, domid, "libxl__devices_destroy failed");
1074 
1075     vm_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/vm", dom_path));
1076     if (vm_path)
1077         if (!xs_rm(ctx->xsh, XBT_NULL, vm_path))
1078             LOGED(ERROR, domid, "xs_rm failed for %s", vm_path);
1079 
1080     if (!xs_rm(ctx->xsh, XBT_NULL, dom_path))
1081         LOGED(ERROR, domid, "xs_rm failed for %s", dom_path);
1082 
1083     xs_rm(ctx->xsh, XBT_NULL, libxl__xs_libxl_path(gc, domid));
1084     xs_rm(ctx->xsh, XBT_NULL, GCSPRINTF( "/local/domain/%d/hvmloader", domid));
1085 
1086     /* This is async operation, we already hold CTX lock */
1087     lock = libxl__lock_domain_userdata(gc, domid);
1088     if (!lock) {
1089         rc = ERROR_LOCK_FAIL;
1090         goto out;
1091     }
1092     libxl__userdata_destroyall(gc, domid);
1093 
1094     libxl__unlock_domain_userdata(lock);
1095 
1096     /* Clean up qemu-save and qemu-resume files. They are
1097      * intermediate files created by libxc. Unfortunately they
1098      * don't fit in existing userdata scheme very well. In soft reset
1099      * case we need to keep the file.
1100      */
1101     if (!dis->soft_reset) {
1102         rc = libxl__remove_file(gc,
1103                                 libxl__device_model_savefile(gc, domid));
1104         if (rc < 0) goto out;
1105     }
1106     rc = libxl__remove_file(gc,
1107              GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%u", domid));
1108     if (rc < 0) goto out;
1109 
1110     rc = libxl__ev_child_fork(gc, &dis->destroyer, domain_destroy_domid_cb);
1111     if (rc < 0) goto out;
1112     if (!rc) { /* child */
1113         ctx->xch = xc_interface_open(ctx->lg,0,0);
1114         if (!ctx->xch) goto badchild;
1115 
1116         if (!dis->soft_reset) {
1117             rc = xc_domain_destroy(ctx->xch, domid);
1118         } else {
1119             rc = xc_domain_pause(ctx->xch, domid);
1120             if (rc < 0) goto badchild;
1121             rc = xc_domain_soft_reset(ctx->xch, domid);
1122             if (rc < 0) goto badchild;
1123             rc = xc_domain_unpause(ctx->xch, domid);
1124         }
1125         if (rc < 0) goto badchild;
1126         _exit(0);
1127 
1128     badchild:
1129         if (errno > 0  && errno < 126) {
1130             _exit(errno);
1131         } else {
1132             LOGED(ERROR, domid,
1133  "xc_domain_destroy failed (with difficult errno value %d)",
1134                   errno);
1135             _exit(-1);
1136         }
1137     }
1138     LOGD(DEBUG, domid, "Forked pid %ld for destroy of domain", (long)rc);
1139 
1140     return;
1141 
1142 out:
1143     dis->callback(egc, dis, rc);
1144     return;
1145 }
1146 
domain_destroy_domid_cb(libxl__egc * egc,libxl__ev_child * destroyer,pid_t pid,int status)1147 static void domain_destroy_domid_cb(libxl__egc *egc,
1148                                     libxl__ev_child *destroyer,
1149                                     pid_t pid, int status)
1150 {
1151     libxl__destroy_domid_state *dis = CONTAINER_OF(destroyer, *dis, destroyer);
1152     STATE_AO_GC(dis->ao);
1153     int rc;
1154 
1155     if (status) {
1156         if (WIFEXITED(status) && WEXITSTATUS(status)<126) {
1157             LOGEVD(ERROR, WEXITSTATUS(status), dis->domid,
1158                    "xc_domain_destroy failed");
1159         } else {
1160             libxl_report_child_exitstatus(CTX, XTL_ERROR,
1161                                           "async domain destroy", pid, status);
1162         }
1163         rc = ERROR_FAIL;
1164         goto out;
1165     }
1166     rc = 0;
1167 
1168  out:
1169     dis->callback(egc, dis, rc);
1170 }
1171 
libxl__get_domid(libxl__gc * gc,uint32_t * domid)1172 int libxl__get_domid(libxl__gc *gc, uint32_t *domid)
1173 {
1174     int rc;
1175     const char *xs_domid;
1176 
1177     rc = libxl__xs_read_checked(gc, XBT_NULL, DOMID_XS_PATH, &xs_domid);
1178     if (rc) goto out;
1179     if (!xs_domid) {
1180         LOG(ERROR, "failed to get own domid (%s)", DOMID_XS_PATH);
1181         rc = ERROR_FAIL;
1182         goto out;
1183     }
1184 
1185     *domid = atoi(xs_domid);
1186 
1187 out:
1188     return rc;
1189 }
1190 
libxl__resolve_domid(libxl__gc * gc,const char * name,uint32_t * domid)1191 int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
1192 {
1193     if (!name)
1194         return 0;
1195     return libxl_domain_qualifier_to_domid(CTX, name, domid);
1196 }
1197 
libxl_list_vcpu(libxl_ctx * ctx,uint32_t domid,int * nr_vcpus_out,int * nr_cpus_out)1198 libxl_vcpuinfo *libxl_list_vcpu(libxl_ctx *ctx, uint32_t domid,
1199                                        int *nr_vcpus_out, int *nr_cpus_out)
1200 {
1201     GC_INIT(ctx);
1202     libxl_vcpuinfo *ptr, *ret;
1203     xc_domaininfo_t domaininfo;
1204     xc_vcpuinfo_t vcpuinfo;
1205 
1206     if (xc_domain_getinfolist(ctx->xch, domid, 1, &domaininfo) != 1) {
1207         LOGED(ERROR, domid, "Getting infolist");
1208         GC_FREE;
1209         return NULL;
1210     }
1211 
1212     if (domaininfo.max_vcpu_id == XEN_INVALID_MAX_VCPU_ID) {
1213         GC_FREE;
1214         return NULL;
1215     }
1216 
1217     *nr_cpus_out = libxl_get_max_cpus(ctx);
1218     ret = ptr = libxl__calloc(NOGC, domaininfo.max_vcpu_id + 1,
1219                               sizeof(libxl_vcpuinfo));
1220 
1221     for (*nr_vcpus_out = 0;
1222          *nr_vcpus_out <= domaininfo.max_vcpu_id;
1223          ++*nr_vcpus_out, ++ptr) {
1224         libxl_bitmap_init(&ptr->cpumap);
1225         if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap, 0))
1226             goto err;
1227         libxl_bitmap_init(&ptr->cpumap_soft);
1228         if (libxl_cpu_bitmap_alloc(ctx, &ptr->cpumap_soft, 0))
1229             goto err;
1230         if (xc_vcpu_getinfo(ctx->xch, domid, *nr_vcpus_out, &vcpuinfo) == -1) {
1231             LOGED(ERROR, domid, "Getting vcpu info");
1232             goto err;
1233         }
1234 
1235         if (xc_vcpu_getaffinity(ctx->xch, domid, *nr_vcpus_out,
1236                                 ptr->cpumap.map, ptr->cpumap_soft.map,
1237                                 XEN_VCPUAFFINITY_SOFT|XEN_VCPUAFFINITY_HARD) == -1) {
1238             LOGED(ERROR, domid, "Getting vcpu affinity");
1239             goto err;
1240         }
1241         ptr->vcpuid = *nr_vcpus_out;
1242         ptr->cpu = vcpuinfo.cpu;
1243         ptr->online = !!vcpuinfo.online;
1244         ptr->blocked = !!vcpuinfo.blocked;
1245         ptr->running = !!vcpuinfo.running;
1246         ptr->vcpu_time = vcpuinfo.cpu_time;
1247     }
1248     GC_FREE;
1249     return ret;
1250 
1251 err:
1252     libxl_bitmap_dispose(&ptr->cpumap);
1253     libxl_bitmap_dispose(&ptr->cpumap_soft);
1254     free(ret);
1255     GC_FREE;
1256     return NULL;
1257 }
1258 
libxl__set_vcpuonline_xenstore(libxl__gc * gc,uint32_t domid,libxl_bitmap * cpumap,const libxl_dominfo * info)1259 static int libxl__set_vcpuonline_xenstore(libxl__gc *gc, uint32_t domid,
1260                                          libxl_bitmap *cpumap,
1261                                          const libxl_dominfo *info)
1262 {
1263     char *dompath;
1264     xs_transaction_t t;
1265     int i, rc = ERROR_FAIL;
1266 
1267     if (!(dompath = libxl__xs_get_dompath(gc, domid)))
1268         goto out;
1269 
1270 retry_transaction:
1271     t = xs_transaction_start(CTX->xsh);
1272     for (i = 0; i <= info->vcpu_max_id; i++)
1273         libxl__xs_printf(gc, t,
1274                          GCSPRINTF("%s/cpu/%u/availability", dompath, i),
1275                          "%s", libxl_bitmap_test(cpumap, i) ? "online" : "offline");
1276     if (!xs_transaction_end(CTX->xsh, t, 0)) {
1277         if (errno == EAGAIN)
1278             goto retry_transaction;
1279     } else
1280         rc = 0;
1281 out:
1282     return rc;
1283 }
1284 
libxl__set_vcpuonline_qmp(libxl__gc * gc,uint32_t domid,libxl_bitmap * cpumap,const libxl_dominfo * info)1285 static int libxl__set_vcpuonline_qmp(libxl__gc *gc, uint32_t domid,
1286                                      libxl_bitmap *cpumap,
1287                                      const libxl_dominfo *info)
1288 {
1289     int i, rc;
1290     libxl_bitmap current_map, final_map;
1291 
1292     libxl_bitmap_init(&current_map);
1293     libxl_bitmap_init(&final_map);
1294 
1295     libxl_bitmap_alloc(CTX, &current_map, info->vcpu_max_id + 1);
1296     libxl_bitmap_set_none(&current_map);
1297     rc = libxl__qmp_query_cpus(gc, domid, &current_map);
1298     if (rc) {
1299         LOGD(ERROR, domid, "Failed to query cpus");
1300         goto out;
1301     }
1302 
1303     libxl_bitmap_copy_alloc(CTX, &final_map, cpumap);
1304 
1305     libxl_for_each_set_bit(i, current_map)
1306         libxl_bitmap_reset(&final_map, i);
1307 
1308     libxl_for_each_set_bit(i, final_map) {
1309         rc = libxl__qmp_cpu_add(gc, domid, i);
1310         if (rc) {
1311             LOGD(ERROR, domid, "Failed to add cpu %d", i);
1312             goto out;
1313         }
1314     }
1315 
1316     rc = 0;
1317 out:
1318     libxl_bitmap_dispose(&current_map);
1319     libxl_bitmap_dispose(&final_map);
1320     return rc;
1321 }
1322 
libxl_set_vcpuonline(libxl_ctx * ctx,uint32_t domid,libxl_bitmap * cpumap)1323 int libxl_set_vcpuonline(libxl_ctx *ctx, uint32_t domid, libxl_bitmap *cpumap)
1324 {
1325     GC_INIT(ctx);
1326     int rc, maxcpus;
1327     libxl_dominfo info;
1328 
1329     libxl_dominfo_init(&info);
1330 
1331     rc = libxl_domain_info(CTX, &info, domid);
1332     if (rc < 0) {
1333         LOGED(ERROR, domid, "Getting domain info list");
1334         goto out;
1335     }
1336 
1337     maxcpus = libxl_bitmap_count_set(cpumap);
1338     if (maxcpus > info.vcpu_max_id + 1)
1339     {
1340         LOGED(ERROR, domid, "Requested %d VCPUs, however maxcpus is %d!",
1341               maxcpus, info.vcpu_max_id + 1);
1342         rc = ERROR_FAIL;
1343         goto out;
1344     }
1345 
1346     switch (libxl__domain_type(gc, domid)) {
1347     case LIBXL_DOMAIN_TYPE_HVM:
1348         switch (libxl__device_model_version_running(gc, domid)) {
1349         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1350             break;
1351         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1352             rc = libxl__set_vcpuonline_qmp(gc, domid, cpumap, &info);
1353             break;
1354         default:
1355             rc = ERROR_INVAL;
1356         }
1357         break;
1358     case LIBXL_DOMAIN_TYPE_PVH:
1359     case LIBXL_DOMAIN_TYPE_PV:
1360         break;
1361     default:
1362         rc = ERROR_INVAL;
1363     }
1364 
1365     if (!rc)
1366         rc = libxl__set_vcpuonline_xenstore(gc, domid, cpumap, &info);
1367 
1368 out:
1369     libxl_dominfo_dispose(&info);
1370     GC_FREE;
1371     return rc;
1372 }
1373 
libxl__domain_s3_resume(libxl__gc * gc,int domid)1374 static int libxl__domain_s3_resume(libxl__gc *gc, int domid)
1375 {
1376     int rc = 0;
1377 
1378     switch (libxl__domain_type(gc, domid)) {
1379     case LIBXL_DOMAIN_TYPE_HVM:
1380         switch (libxl__device_model_version_running(gc, domid)) {
1381         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1382             rc = xc_hvm_param_set(CTX->xch, domid, HVM_PARAM_ACPI_S_STATE, 0);
1383             break;
1384         case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1385             rc = libxl__qmp_system_wakeup(gc, domid);
1386             break;
1387         default:
1388             rc = ERROR_INVAL;
1389             break;
1390         }
1391         break;
1392     default:
1393         rc = ERROR_INVAL;
1394         break;
1395     }
1396 
1397     return rc;
1398 }
1399 
libxl_send_trigger(libxl_ctx * ctx,uint32_t domid,libxl_trigger trigger,uint32_t vcpuid)1400 int libxl_send_trigger(libxl_ctx *ctx, uint32_t domid,
1401                        libxl_trigger trigger, uint32_t vcpuid)
1402 {
1403     int rc;
1404     GC_INIT(ctx);
1405 
1406     switch (trigger) {
1407     case LIBXL_TRIGGER_POWER:
1408         rc = xc_domain_send_trigger(ctx->xch, domid,
1409                                     XEN_DOMCTL_SENDTRIGGER_POWER, vcpuid);
1410         break;
1411     case LIBXL_TRIGGER_SLEEP:
1412         rc = xc_domain_send_trigger(ctx->xch, domid,
1413                                     XEN_DOMCTL_SENDTRIGGER_SLEEP, vcpuid);
1414         break;
1415     case LIBXL_TRIGGER_NMI:
1416         rc = xc_domain_send_trigger(ctx->xch, domid,
1417                                     XEN_DOMCTL_SENDTRIGGER_NMI, vcpuid);
1418         break;
1419     case LIBXL_TRIGGER_INIT:
1420         rc = xc_domain_send_trigger(ctx->xch, domid,
1421                                     XEN_DOMCTL_SENDTRIGGER_INIT, vcpuid);
1422         break;
1423     case LIBXL_TRIGGER_RESET:
1424         rc = xc_domain_send_trigger(ctx->xch, domid,
1425                                     XEN_DOMCTL_SENDTRIGGER_RESET, vcpuid);
1426         break;
1427     case LIBXL_TRIGGER_S3RESUME:
1428         rc = libxl__domain_s3_resume(gc, domid);
1429         break;
1430     default:
1431         rc = -1;
1432         errno = EINVAL;
1433         break;
1434     }
1435 
1436     if (rc != 0) {
1437         LOGED(ERROR, domid, "Send trigger '%s' failed",
1438               libxl_trigger_to_string(trigger));
1439         rc = ERROR_FAIL;
1440     }
1441 
1442     GC_FREE;
1443     return rc;
1444 }
1445 
libxl_vm_get_start_time(libxl_ctx * ctx,uint32_t domid)1446 uint32_t libxl_vm_get_start_time(libxl_ctx *ctx, uint32_t domid)
1447 {
1448     GC_INIT(ctx);
1449     char *dompath = libxl__xs_get_dompath(gc, domid);
1450     char *vm_path, *start_time;
1451     uint32_t ret;
1452 
1453     vm_path = libxl__xs_read(
1454         gc, XBT_NULL, GCSPRINTF("%s/vm", dompath));
1455     start_time = libxl__xs_read(
1456         gc, XBT_NULL, GCSPRINTF("%s/start_time", vm_path));
1457     if (start_time == NULL) {
1458         LOGEVD(ERROR, -1, domid, "Can't get start time of domain");
1459         ret = -1;
1460     }else{
1461         ret = strtoul(start_time, NULL, 10);
1462     }
1463     GC_FREE;
1464     return ret;
1465 }
1466 
1467 /* For QEMU upstream we always need to provide the number of cpus present to
1468  * QEMU whether they are online or not; otherwise QEMU won't accept the saved
1469  * state. See implementation of libxl__qmp_query_cpus.
1470  */
libxl__update_avail_vcpus_qmp(libxl__gc * gc,uint32_t domid,unsigned int max_vcpus,libxl_bitmap * map)1471 static int libxl__update_avail_vcpus_qmp(libxl__gc *gc, uint32_t domid,
1472                                          unsigned int max_vcpus,
1473                                          libxl_bitmap *map)
1474 {
1475     int rc;
1476 
1477     rc = libxl__qmp_query_cpus(gc, domid, map);
1478     if (rc) {
1479         LOGD(ERROR, domid, "Fail to get number of cpus");
1480         goto out;
1481     }
1482 
1483     rc = 0;
1484 out:
1485     return rc;
1486 }
1487 
libxl__update_avail_vcpus_xenstore(libxl__gc * gc,uint32_t domid,unsigned int max_vcpus,libxl_bitmap * map)1488 static int libxl__update_avail_vcpus_xenstore(libxl__gc *gc, uint32_t domid,
1489                                               unsigned int max_vcpus,
1490                                               libxl_bitmap *map)
1491 {
1492     int rc;
1493     unsigned int i;
1494     const char *dompath;
1495 
1496     dompath = libxl__xs_get_dompath(gc, domid);
1497     if (!dompath) {
1498         rc = ERROR_FAIL;
1499         goto out;
1500     }
1501 
1502     for (i = 0; i < max_vcpus; i++) {
1503         const char *path = GCSPRINTF("%s/cpu/%u/availability", dompath, i);
1504         const char *content;
1505         rc = libxl__xs_read_checked(gc, XBT_NULL, path, &content);
1506         if (rc) goto out;
1507         if (content && !strcmp(content, "online"))
1508             libxl_bitmap_set(map, i);
1509     }
1510 
1511     rc = 0;
1512 out:
1513     return rc;
1514 }
1515 
libxl_retrieve_domain_configuration(libxl_ctx * ctx,uint32_t domid,libxl_domain_config * d_config)1516 int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
1517                                         libxl_domain_config *d_config)
1518 {
1519     GC_INIT(ctx);
1520     int rc;
1521     libxl__domain_userdata_lock *lock = NULL;
1522 
1523     CTX_LOCK;
1524 
1525     lock = libxl__lock_domain_userdata(gc, domid);
1526     if (!lock) {
1527         rc = ERROR_LOCK_FAIL;
1528         goto out;
1529     }
1530 
1531     rc = libxl__get_domain_configuration(gc, domid, d_config);
1532     if (rc) {
1533         LOGD(ERROR, domid, "Fail to get domain configuration");
1534         rc = ERROR_FAIL;
1535         goto out;
1536     }
1537 
1538     /* Domain name */
1539     {
1540         char *domname;
1541         domname = libxl_domid_to_name(ctx, domid);
1542         if (!domname) {
1543             LOGD(ERROR, domid, "Fail to get domain name");
1544             goto out;
1545         }
1546         free(d_config->c_info.name);
1547         d_config->c_info.name = domname; /* steals allocation */
1548     }
1549 
1550     /* Domain UUID */
1551     {
1552         libxl_dominfo info;
1553         libxl_dominfo_init(&info);
1554         rc = libxl_domain_info(ctx, &info, domid);
1555         if (rc) {
1556             LOGD(ERROR, domid, "Fail to get domain info");
1557             libxl_dominfo_dispose(&info);
1558             goto out;
1559         }
1560         libxl_uuid_copy(ctx, &d_config->c_info.uuid, &info.uuid);
1561         libxl_dominfo_dispose(&info);
1562     }
1563 
1564     /* VCPUs */
1565     {
1566         libxl_bitmap *map = &d_config->b_info.avail_vcpus;
1567         unsigned int max_vcpus = d_config->b_info.max_vcpus;
1568         libxl_device_model_version version;
1569 
1570         libxl_bitmap_dispose(map);
1571         libxl_bitmap_init(map);
1572         libxl_bitmap_alloc(CTX, map, max_vcpus);
1573         libxl_bitmap_set_none(map);
1574 
1575         switch (d_config->b_info.type) {
1576         case LIBXL_DOMAIN_TYPE_HVM:
1577             version = libxl__device_model_version_running(gc, domid);
1578             assert(version != LIBXL_DEVICE_MODEL_VERSION_UNKNOWN);
1579             switch (version) {
1580             case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
1581                 rc = libxl__update_avail_vcpus_qmp(gc, domid,
1582                                                    max_vcpus, map);
1583                 break;
1584             case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
1585                 rc = libxl__update_avail_vcpus_xenstore(gc, domid,
1586                                                         max_vcpus, map);
1587                 break;
1588             default:
1589                 abort();
1590             }
1591             break;
1592         case LIBXL_DOMAIN_TYPE_PVH:
1593         case LIBXL_DOMAIN_TYPE_PV:
1594             rc = libxl__update_avail_vcpus_xenstore(gc, domid,
1595                                                     max_vcpus, map);
1596             break;
1597         default:
1598             abort();
1599         }
1600 
1601         if (rc) {
1602             LOGD(ERROR, domid, "Fail to update available cpu map");
1603             goto out;
1604         }
1605     }
1606 
1607     /* Memory limits:
1608      *
1609      * Currently there are three memory limits:
1610      *  1. "target" in xenstore (originally memory= in config file)
1611      *  2. "static-max" in xenstore (originally maxmem= in config file)
1612      *  3. "max_memkb" in hypervisor
1613      *
1614      * The third one is not visible and currently managed by
1615      * toolstack. In order to rebuild a domain we only need to have
1616      * "target" and "static-max".
1617      */
1618     {
1619         uint64_t target_memkb = 0, max_memkb = 0;
1620 
1621         /* "target" */
1622         rc = libxl__get_memory_target(gc, domid, &target_memkb, &max_memkb);
1623         if (rc) {
1624             LOGD(ERROR, domid, "Fail to get memory target");
1625             goto out;
1626         }
1627 
1628         /* libxl__get_targetmem_fudge() calculates the difference from
1629          * what is in xenstore to what we have in the domain build info.
1630          */
1631         d_config->b_info.target_memkb = target_memkb +
1632             libxl__get_targetmem_fudge(gc, &d_config->b_info);
1633 
1634         d_config->b_info.max_memkb = max_memkb;
1635     }
1636 
1637     /* Scheduler params */
1638     {
1639         libxl_domain_sched_params_dispose(&d_config->b_info.sched_params);
1640         rc = libxl_domain_sched_params_get(ctx, domid,
1641                                            &d_config->b_info.sched_params);
1642         if (rc) {
1643             LOGD(ERROR, domid, "Fail to get scheduler parameters");
1644             goto out;
1645         }
1646     }
1647 
1648     /* Devices: disk, nic, vtpm, pcidev etc. */
1649 
1650     /* The MERGE macro implements following logic:
1651      * 0. retrieve JSON (done by now)
1652      * 1. retrieve list of device from xenstore
1653      * 2. use xenstore entries as primary reference and compare JSON
1654      *    entries with them.
1655      *    a. if a device is present in xenstore and in JSON, merge the
1656      *       two views.
1657      *    b. if a device is not present in xenstore but in JSON, delete
1658      *       it from the result.
1659      *    c. it's impossible to have an entry present in xenstore but
1660      *       not in JSON, because we maintain an invariant that every
1661      *       entry in xenstore must have a corresponding entry in JSON.
1662      * 3. "merge" operates on "src" and "dst". "src" points to the
1663      *    entry retrieved from xenstore while "dst" points to the entry
1664      *    retrieve from JSON.
1665      */
1666     {
1667         const struct libxl_device_type *dt;
1668         int idx;
1669 
1670         for (idx = 0;; idx++) {
1671             void *p = NULL;
1672             void **devs;
1673             int i, j, num;
1674             int *num_dev;
1675 
1676             dt = device_type_tbl[idx];
1677             if (!dt)
1678                 break;
1679 
1680             if (!dt->compare)
1681                 continue;
1682 
1683             num_dev = libxl__device_type_get_num(dt, d_config);
1684             p = libxl__device_list(gc, dt, domid, &num);
1685             if (p == NULL) {
1686                 LOGD(DEBUG, domid, "No %s from xenstore",
1687                      dt->type);
1688             }
1689             devs = libxl__device_type_get_ptr(dt, d_config);
1690 
1691             for (i = 0; i < *num_dev; i++) {
1692                 void *q;
1693 
1694                 q = libxl__device_type_get_elem(dt, d_config, i);
1695                 for (j = 0; j < num; j++) {
1696                     if (dt->compare(p + dt->dev_elem_size * j, q))
1697                         break;
1698                 }
1699 
1700                 if (j < num) {         /* found in xenstore */
1701                     if (dt->merge)
1702                         dt->merge(ctx, p + dt->dev_elem_size * j, q);
1703                 } else {                /* not found in xenstore */
1704                     LOGD(WARN, domid,
1705                          "Device present in JSON but not in xenstore, ignored");
1706 
1707                     dt->dispose(q);
1708 
1709                     for (j = i; j < *num_dev - 1; j++)
1710                         memcpy(libxl__device_type_get_elem(dt, d_config, j),
1711                                libxl__device_type_get_elem(dt, d_config, j+1),
1712                                dt->dev_elem_size);
1713 
1714                     /* rewind counters */
1715                     (*num_dev)--;
1716                     i--;
1717 
1718                     *devs = libxl__realloc(NOGC, *devs,
1719                                            dt->dev_elem_size * *num_dev);
1720                 }
1721             }
1722 
1723             for (i = 0; i < num; i++)
1724                 dt->dispose(p + dt->dev_elem_size * i);
1725             free(p);
1726         }
1727     }
1728 
1729 out:
1730     if (lock) libxl__unlock_domain_userdata(lock);
1731     CTX_UNLOCK;
1732     GC_FREE;
1733     return rc;
1734 }
1735 
1736 /*
1737  * Local variables:
1738  * mode: C
1739  * c-basic-offset: 4
1740  * indent-tabs-mode: nil
1741  * End:
1742  */
1743