/* * The Qubes OS Project, http://www.qubes-os.org * * Copyright (C) 2010 Rafal Wojtczuk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include static struct libvchan *ctrl; static int is_server; int write_all_vchan_ext(const void *buf, int size) { int written = 0; int ret; while (written < size) { ret = libvchan_write(ctrl, (const char *) buf + written, size - written); if (ret <= 0) { perror("write"); exit(1); } written += ret; } // fprintf(stderr, "sent %d bytes\n", size); return size; } int read_all_vchan_ext(void *buf, int size) { int written = 0; int ret; while (written < size) { ret = libvchan_read(ctrl, (char *) buf + written, size - written); if (ret == 0) { fprintf(stderr, "EOF\n"); exit(1); } if (ret < 0) { perror("read"); exit(1); } written += ret; } // fprintf(stderr, "read %d bytes\n", size); return size; } unsigned int read_ready_vchan_ext() { int ready = libvchan_data_ready(ctrl); if (ready < 0) { fprintf(stderr, "libvchan_data_ready returned invalid size\n"); exit(1); } return ready; } unsigned int buffer_space_vchan_ext() { int space = libvchan_buffer_space(ctrl); if (space < 0) { fprintf(stderr, "libvchan_buffer_space returned invalid size\n"); exit(1); } return space; } // if the remote domain is destroyed, we get no notification // thus, we check for the status periodically #ifdef XENCTRL_HAS_XC_INTERFACE static xc_interface *xc_handle = NULL; #else static int xc_handle = -1; #endif void slow_check_for_libvchan_is_eof(struct libvchan *ctrl) { struct evtchn_status evst; evst.port = ctrl->evport; evst.dom = DOMID_SELF; if (xc_evtchn_status(xc_handle, &evst)) { perror("xc_evtchn_status"); exit(1); } if (evst.status != EVTCHNSTAT_interdomain) { fprintf(stderr, "event channel disconnected\n"); exit(0); } } int wait_for_vchan_or_argfd_once(int max, fd_set * rdset, fd_set * wrset) { int vfd, ret; struct timespec tv = { 1, 100000000 }; sigset_t empty_set; sigemptyset(&empty_set); vfd = libvchan_fd_for_select(ctrl); FD_SET(vfd, rdset); if (vfd > max) max = vfd; max++; ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set); if (ret < 0) { if (errno != EINTR) { perror("select"); exit(1); } else { FD_ZERO(rdset); FD_ZERO(wrset); fprintf(stderr, "eintr\n"); return 1; } } if (libvchan_is_eof(ctrl)) { fprintf(stderr, "libvchan_is_eof\n"); exit(0); } if (!is_server && ret == 0) slow_check_for_libvchan_is_eof(ctrl); if (FD_ISSET(vfd, rdset)) // the following will never block; we need to do this to // clear libvchan_fd pending state libvchan_wait(ctrl); return ret; } void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset) { fd_set r = *rdset, w = *wrset; do { *rdset = r; *wrset = w; } while (wait_for_vchan_or_argfd_once(max, rdset, wrset) == 0); } int peer_server_init(int port) { is_server = 1; ctrl = libvchan_server_init(port); if (!ctrl) { perror("libvchan_server_init"); exit(1); } return 0; } char *peer_client_init(int dom, int port) { struct xs_handle *xs; char buf[64]; char *name; char *dummy, *dummy2; unsigned int len = 0; char devbuf[128]; char dombuf[128]; unsigned int count; char **vec; // double_buffered = 1; // writes to vchan are buffered, nonblocking // double_buffer_init(); xs = xs_daemon_open(); if (!xs) { perror("xs_daemon_open"); exit(1); } snprintf(buf, sizeof(buf), "/local/domain/%d/name", dom); name = xs_read(xs, 0, buf, &len); if (!name) { perror("xs_read domainname"); exit(1); } snprintf(devbuf, sizeof(devbuf), "/local/domain/%d/device/vchan/%d/event-channel", dom, port); snprintf(dombuf, sizeof(dombuf), "/local/domain/%d", dom); xs_watch(xs, devbuf, devbuf); do { vec = xs_read_watch(xs, &count); if (vec) free(vec); len = 0; dummy = xs_read(xs, 0, devbuf, &len); if (dummy) free(dummy); else { /* check if domain still alive */ dummy2 = xs_read(xs, 0, dombuf, &len); if (!dummy2) { fprintf(stderr, "domain dead\n"); exit(1); } free(dummy2); } } while (!dummy || !len); // wait for the server to create xenstore entries xs_daemon_close(xs); // now client init should succeed; "while" is redundant while (!(ctrl = libvchan_client_init(dom, port))); #ifdef XENCTRL_HAS_XC_INTERFACE xc_handle = xc_interface_open(NULL, 0, 0); if (!xc_handle) { #else xc_handle = xc_interface_open(); if (xc_handle < 0) { #endif perror("xc_interface_open"); exit(1); } return name; }