int libvchan_wait(libvchan_t *ctrl) { int ret = -2; /* invalid, so can be distinguished from real libxenvchan_wait return code */ struct xs_handle *xs; if (ctrl->xenvchan->is_server && libxenvchan_is_open(ctrl->xenvchan) == 2) { /* In case of vchan server waiting for a client, we'll not receive any * notification if the remote domain dies before connecting. Because of * that, check periodically if remote domain is still alive while * waiting for a connection. Actually this doesn't cover all the cases * - if remote domain is still alive, but remote process dies before * connecting, we'll also not receive any notification. But this, in * most cases, can be solved by application using libvchan. * * During normal operation this shouldn't be long - in most cases vchan * client will connect almost instantly. So this sleep(10) loop will * not hurt. Alternativelly it could be implemented with * xs_watch("@releaseDomain"), but such approach will slow down most * common execution path (xs_open+xs_watch even if client connects * right away). */ while (ret == -2 && libxenvchan_is_open(ctrl->xenvchan) == 2) { fd_set rd_set; struct timeval tv = { 10, 0 }; int vchan_fd = libxenvchan_fd_for_select(ctrl->xenvchan); FD_ZERO(&rd_set); FD_SET(vchan_fd, &rd_set); switch (select(vchan_fd+1, &rd_set, NULL, NULL, &tv)) { case 0: if (!libvchan__check_domain_alive(ctrl->xc_handle, ctrl->remote_domain)) return -1; break; case 1: /* break the loop */ ret = -1; break; default: if (errno == EINTR) break; perror("select"); return -1; } } } ret = libxenvchan_wait(ctrl->xenvchan); if (ctrl->xs_path) { /* remove xenstore entry at first client connection */ xs = xs_open(0); if (xs) { /* if xenstore connection failed just do not remove entries, but do * not abort whole function, especially still free the memory */ xs_rm(xs, 0, ctrl->xs_path); xs_close(xs); } free(ctrl->xs_path); ctrl->xs_path = NULL; } return ret; }
int libvchan_is_open(libvchan_t *ctrl) { int ret; struct evtchn_status evst; ret = libxenvchan_is_open(ctrl->xenvchan); if (ret == 2) { if (!libvchan__check_domain_alive(ctrl->xc_handle, ctrl->remote_domain)) return VCHAN_DISCONNECTED; return VCHAN_WAITING; } if (!ret) return VCHAN_DISCONNECTED; /* slow check in case of domain destroy */ evst.port = ctrl->xenvchan->event_port; evst.dom = DOMID_SELF; if (xc_evtchn_status(ctrl->xc_handle, &evst)) { perror("xc_evtchn_status"); return VCHAN_DISCONNECTED; } if (evst.status != EVTCHNSTAT_interdomain) { if (!ctrl->xenvchan->is_server) ctrl->xenvchan->ring->srv_live = 0; return VCHAN_DISCONNECTED; } return VCHAN_CONNECTED; }
libvchan_t *libvchan_client_init(int domain, int port) { char xs_path[255]; char xs_path_watch[255]; libvchan_t *ctrl; xc_interface *xc_handle; struct xs_handle *xs; char **vec; unsigned int count, len; char *dummy = NULL; char *own_domid = NULL; xc_handle = xc_interface_open(NULL, NULL, 0); if (!xc_handle) { /* error already logged by xc_interface_open */ goto err; } /* wait for server to appear */ xs = xs_open(0); if (!xs) { perror("xs_open"); goto err_xc; } len = 0; if (!xs_watch(xs, "domid", "domid")) { fprintf(stderr, "Cannot setup xenstore watch\n"); goto err_xs; } if (!xs_watch(xs, "@releaseDomain", "release")) { fprintf(stderr, "Cannot setup xenstore watch\n"); goto err_xs; } while (!dummy || !len) { vec = xs_read_watch(xs, &count); if (vec) { if (strcmp(vec[XS_WATCH_TOKEN], "domid") == 0) { /* domid have changed */ if (own_domid) { free(own_domid); own_domid = NULL; xs_unwatch(xs, xs_path_watch, xs_path_watch); } } free(vec); } if (!own_domid) { /* construct xenstore path on first iteration and on every domid * change detected (save+restore case) */ own_domid = xs_read(xs, 0, "domid", &len); if (!own_domid) { fprintf(stderr, "Cannot get own domid\n"); goto err_xs; } if (atoi(own_domid) == domain) { fprintf(stderr, "Loopback vchan connection not supported\n"); free(own_domid); goto err_xs; } snprintf(xs_path, sizeof(xs_path), "/local/domain/%d/data/vchan/%s/%d", domain, own_domid, port); /* watch on this key as we might not have access to the whole directory */ snprintf(xs_path_watch, sizeof(xs_path_watch), "%s/event-channel", xs_path); if (!xs_watch(xs, xs_path_watch, xs_path_watch)) { fprintf(stderr, "Cannot setup watch on %s\n", xs_path_watch); free(own_domid); goto err_xs; } } dummy = xs_read(xs, 0, xs_path_watch, &len); if (dummy) free(dummy); else { if (!libvchan__check_domain_alive(xc_handle, domain)) { fprintf(stderr, "domain dead\n"); goto err_xs; } } } if (own_domid) free(own_domid); xs_close(xs); ctrl = malloc(sizeof(*ctrl)); if (!ctrl) return NULL; ctrl->xs_path = NULL; ctrl->xenvchan = libxenvchan_client_init(NULL, domain, xs_path); if (!ctrl->xenvchan) { free(ctrl); return NULL; } ctrl->xenvchan->blocking = 1; /* notify server */ xc_evtchn_notify(ctrl->xenvchan->event, ctrl->xenvchan->event_port); ctrl->remote_domain = domain; ctrl->xc_handle = xc_handle; return ctrl; err_xs: xs_close(xs); err_xc: xc_interface_close(xc_handle); err: return NULL; }