-Tue Jun 26 15:11:00 EST 2007 Daniel P. Berrange <berrange@rdhat.com>
+Tue Jun 26 16:41:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
+
+ * qemud/driver.c, qemud/driver.h, qemud/internal.h,
+ qemud/qemud.c: Move VM & network process lifecycle management
+ out of qemud.c and into the main driver.c
+
+Tue Jun 26 15:11:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
* qemud/dispatch.c, qemud/driver.c, qemud/driver.h: Move
code for generating capabilities XML into driver.c file
alongside other driver APIs
-Tue Jun 26 14:52:00 EST 2007 Daniel P. Berrange <berrange@rdhat.com>
+Tue Jun 26 14:52:00 EST 2007 Daniel P. Berrange <berrange@redhat.com>
* qemud/event.c, qemud/event.h, qemud/Makefile.am: Generic
standalone event loop implementation for monitoring file
#include <unistd.h>
#include <errno.h>
#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <paths.h>
+#include <ctype.h>
+#include <sys/wait.h>
#include <libvirt/virterror.h>
+#include "event.h"
#include "buf.h"
#include "internal.h"
#include "driver.h"
}
}
+static void qemudDispatchVMEvent(int fd, int events, void *opaque);
+
+static int qemudSetCloseExec(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFD)) < 0)
+ goto error;
+ flags |= FD_CLOEXEC;
+ if ((fcntl(fd, F_SETFD, flags)) < 0)
+ goto error;
+ return 0;
+ error:
+ qemudLog(QEMUD_ERR, "Failed to set close-on-exec file descriptor flag");
+ return -1;
+}
+
+
+static int qemudSetNonBlock(int fd) {
+ int flags;
+ if ((flags = fcntl(fd, F_GETFL)) < 0)
+ goto error;
+ flags |= O_NONBLOCK;
+ if ((fcntl(fd, F_SETFL, flags)) < 0)
+ goto error;
+ return 0;
+ error:
+ qemudLog(QEMUD_ERR, "Failed to set non-blocking file descriptor flag");
+ return -1;
+}
+
+
+void qemudShutdown(struct qemud_server *server) {
+ struct qemud_vm *vm;
+ struct qemud_network *network;
+
+ /* shutdown active VMs */
+ vm = server->vms;
+ while (vm) {
+ struct qemud_vm *next = vm->next;
+ if (qemudIsActiveVM(vm))
+ qemudShutdownVMDaemon(server, vm);
+ vm = next;
+ }
+
+ /* free inactive VMs */
+ vm = server->vms;
+ while (vm) {
+ struct qemud_vm *next = vm->next;
+ qemudFreeVM(vm);
+ vm = next;
+ }
+ server->vms = NULL;
+ server->nactivevms = 0;
+ server->ninactivevms = 0;
+
+ /* shutdown active networks */
+ network = server->networks;
+ while (network) {
+ struct qemud_network *next = network->next;
+ if (qemudIsActiveNetwork(network))
+ qemudShutdownNetworkDaemon(server, network);
+ network = next;
+ }
+
+ /* free inactive networks */
+ network = server->networks;
+ while (network) {
+ struct qemud_network *next = network->next;
+ qemudFreeNetwork(network);
+ network = next;
+ }
+ server->networks = NULL;
+ server->nactivenetworks = 0;
+ server->ninactivenetworks = 0;
+}
+
+static int
+qemudExec(struct qemud_server *server, char **argv,
+ int *retpid, int *outfd, int *errfd) {
+ int pid, null;
+ int pipeout[2] = {-1,-1};
+ int pipeerr[2] = {-1,-1};
+
+ if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open %s : %s",
+ _PATH_DEVNULL, strerror(errno));
+ goto cleanup;
+ }
+
+ if ((outfd != NULL && pipe(pipeout) < 0) ||
+ (errfd != NULL && pipe(pipeerr) < 0)) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe : %s",
+ strerror(errno));
+ goto cleanup;
+ }
+
+ if ((pid = fork()) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot fork child process : %s",
+ strerror(errno));
+ goto cleanup;
+ }
+
+ if (pid) { /* parent */
+ close(null);
+ if (outfd) {
+ close(pipeout[1]);
+ qemudSetNonBlock(pipeout[0]);
+ qemudSetCloseExec(pipeout[0]);
+ *outfd = pipeout[0];
+ }
+ if (errfd) {
+ close(pipeerr[1]);
+ qemudSetNonBlock(pipeerr[0]);
+ qemudSetCloseExec(pipeerr[0]);
+ *errfd = pipeerr[0];
+ }
+ *retpid = pid;
+ return 0;
+ }
+
+ /* child */
+
+ if (pipeout[0] > 0 && close(pipeout[0]) < 0)
+ _exit(1);
+ if (pipeerr[0] > 0 && close(pipeerr[0]) < 0)
+ _exit(1);
+
+ if (dup2(null, STDIN_FILENO) < 0)
+ _exit(1);
+ if (dup2(pipeout[1] > 0 ? pipeout[1] : null, STDOUT_FILENO) < 0)
+ _exit(1);
+ if (dup2(pipeerr[1] > 0 ? pipeerr[1] : null, STDERR_FILENO) < 0)
+ _exit(1);
+
+ close(null);
+ if (pipeout[1] > 0)
+ close(pipeout[1]);
+ if (pipeerr[1] > 0)
+ close(pipeerr[1]);
+
+ execvp(argv[0], argv);
+
+ _exit(1);
+
+ return 0;
+
+ cleanup:
+ if (pipeerr[0] > 0)
+ close(pipeerr[0]);
+ if (pipeerr[1] > 0)
+ close(pipeerr[1]);
+ if (pipeout[0] > 0)
+ close(pipeout[0]);
+ if (pipeout[1] > 0)
+ close(pipeout[1]);
+ if (null > 0)
+ close(null);
+ return -1;
+}
+
+/* Return -1 for error, 1 to continue reading and 0 for success */
+typedef int qemudHandlerMonitorOutput(struct qemud_server *server,
+ struct qemud_vm *vm,
+ const char *output,
+ int fd);
+
+static int
+qemudReadMonitorOutput(struct qemud_server *server,
+ struct qemud_vm *vm,
+ int fd,
+ char *buf,
+ int buflen,
+ qemudHandlerMonitorOutput func,
+ const char *what)
+{
+#define MONITOR_TIMEOUT 3000
+
+ int got = 0;
+ buf[0] = '\0';
+
+ /* Consume & discard the initial greeting */
+ while (got < (buflen-1)) {
+ int ret;
+
+ ret = read(fd, buf+got, buflen-got-1);
+ if (ret == 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "QEMU quit during %s startup\n%s", what, buf);
+ return -1;
+ }
+ if (ret < 0) {
+ struct pollfd pfd = { .fd = fd, .events = POLLIN };
+ if (errno == EINTR)
+ continue;
+
+ if (errno != EAGAIN) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failure while reading %s startup output: %s",
+ what, strerror(errno));
+ return -1;
+ }
+
+ ret = poll(&pfd, 1, MONITOR_TIMEOUT);
+ if (ret == 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Timed out while reading %s startup output", what);
+ return -1;
+ } else if (ret == -1) {
+ if (errno != EINTR) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failure while reading %s startup output: %s",
+ what, strerror(errno));
+ return -1;
+ }
+ } else {
+ /* Make sure we continue loop & read any further data
+ available before dealing with EOF */
+ if (pfd.revents & (POLLIN | POLLHUP))
+ continue;
+
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Failure while reading %s startup output", what);
+ return -1;
+ }
+ } else {
+ got += ret;
+ buf[got] = '\0';
+ if ((ret = func(server, vm, buf, fd)) != 1)
+ return ret;
+ }
+ }
+
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Out of space while reading %s startup output", what);
+ return -1;
+
+#undef MONITOR_TIMEOUT
+}
+
+static int
+qemudCheckMonitorPrompt(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_vm *vm,
+ const char *output,
+ int fd)
+{
+ if (strstr(output, "(qemu) ") == NULL)
+ return 1; /* keep reading */
+
+ vm->monitor = fd;
+
+ return 0;
+}
+
+static int qemudOpenMonitor(struct qemud_server *server, struct qemud_vm *vm, const char *monitor) {
+ int monfd;
+ char buf[1024];
+ int ret = -1;
+
+ if (!(monfd = open(monitor, O_RDWR))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Unable to open monitor path %s", monitor);
+ return -1;
+ }
+ if (qemudSetCloseExec(monfd) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Unable to set monitor close-on-exec flag");
+ goto error;
+ }
+ if (qemudSetNonBlock(monfd) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Unable to put monitor into non-blocking mode");
+ goto error;
+ }
+
+ ret = qemudReadMonitorOutput(server, vm, monfd,
+ buf, sizeof(buf),
+ qemudCheckMonitorPrompt,
+ "monitor");
+ error:
+ close(monfd);
+ return ret;
+}
+
+static int qemudExtractMonitorPath(const char *haystack, char *path, int pathmax) {
+ static const char needle[] = "char device redirected to";
+ char *tmp;
+
+ if (!(tmp = strstr(haystack, needle)))
+ return -1;
+
+ strncpy(path, tmp+sizeof(needle), pathmax-1);
+ path[pathmax-1] = '\0';
+
+ while (*path) {
+ /*
+ * The monitor path ends at first whitespace char
+ * so lets search for it & NULL terminate it there
+ */
+ if (isspace(*path)) {
+ *path = '\0';
+ return 0;
+ }
+ path++;
+ }
+
+ /*
+ * We found a path, but didn't find any whitespace,
+ * so it must be still incomplete - we should at
+ * least see a \n
+ */
+ return -1;
+}
+
+static int
+qemudOpenMonitorPath(struct qemud_server *server,
+ struct qemud_vm *vm,
+ const char *output,
+ int fd ATTRIBUTE_UNUSED)
+{
+ char monitor[PATH_MAX];
+
+ if (qemudExtractMonitorPath(output, monitor, sizeof(monitor)) < 0)
+ return 1; /* keep reading */
+
+ return qemudOpenMonitor(server, vm, monitor);
+}
+
+static int qemudWaitForMonitor(struct qemud_server *server, struct qemud_vm *vm) {
+ char buf[1024]; /* Plenty of space to get startup greeting */
+ int ret = qemudReadMonitorOutput(server, vm, vm->stderr,
+ buf, sizeof(buf),
+ qemudOpenMonitorPath,
+ "console");
+
+ buf[sizeof(buf)-1] = '\0';
+ retry:
+ if (write(vm->logfile, buf, strlen(buf)) < 0) {
+ /* Log, but ignore failures to write logfile for VM */
+ if (errno == EINTR)
+ goto retry;
+ qemudLog(QEMUD_WARN, "Unable to log VM console data: %s",
+ strerror(errno));
+ }
+
+ return ret;
+}
+
+static int qemudNextFreeVNCPort(struct qemud_server *server ATTRIBUTE_UNUSED) {
+ int i;
+
+ for (i = 5900 ; i < 6000 ; i++) {
+ int fd;
+ int reuse = 1;
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(i);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) < 0) {
+ close(fd);
+ break;
+ }
+
+ if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+ /* Not in use, lets grab it */
+ close(fd);
+ return i;
+ }
+ close(fd);
+
+ if (errno == EADDRINUSE) {
+ /* In use, try next */
+ continue;
+ }
+ /* Some other bad failure, get out.. */
+ break;
+ }
+ return -1;
+}
+
+int qemudStartVMDaemon(struct qemud_server *server,
+ struct qemud_vm *vm) {
+ char **argv = NULL, **tmp;
+ int i, ret = -1;
+ char logfile[PATH_MAX];
+
+ if (qemudIsActiveVM(vm)) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "VM is already active");
+ return -1;
+ }
+
+ if (vm->def->vncPort < 0) {
+ int port = qemudNextFreeVNCPort(server);
+ if (port < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "Unable to find an unused VNC port");
+ return -1;
+ }
+ vm->def->vncActivePort = port;
+ } else
+ vm->def->vncActivePort = vm->def->vncPort;
+
+ if ((strlen(server->logDir) + /* path */
+ 1 + /* Separator */
+ strlen(vm->def->name) + /* basename */
+ 4 + /* suffix .log */
+ 1 /* NULL */) > PATH_MAX) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "config file path too long: %s/%s.log",
+ server->logDir, vm->def->name);
+ return -1;
+ }
+ strcpy(logfile, server->logDir);
+ strcat(logfile, "/");
+ strcat(logfile, vm->def->name);
+ strcat(logfile, ".log");
+
+ if (qemudEnsureDir(server->logDir) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot create log directory %s: %s",
+ server->logDir, strerror(errno));
+ return -1;
+ }
+
+ if ((vm->logfile = open(logfile, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR)) < 0) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to create logfile %s: %s",
+ logfile, strerror(errno));
+ return -1;
+ }
+
+ if (qemudBuildCommandLine(server, vm, &argv) < 0) {
+ close(vm->logfile);
+ vm->logfile = -1;
+ return -1;
+ }
+
+ tmp = argv;
+ while (*tmp) {
+ if (write(vm->logfile, *tmp, strlen(*tmp)) < 0)
+ qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
+ errno, strerror(errno));
+ if (write(vm->logfile, " ", 1) < 0)
+ qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
+ errno, strerror(errno));
+ tmp++;
+ }
+ if (write(vm->logfile, "\n", 1) < 0)
+ qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
+ errno, strerror(errno));
+
+ if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr) == 0) {
+ vm->id = server->nextvmid++;
+ vm->state = QEMUD_STATE_RUNNING;
+
+ server->ninactivevms--;
+ server->nactivevms++;
+
+ virEventAddHandle(vm->stdout,
+ POLLIN | POLLERR | POLLHUP,
+ qemudDispatchVMEvent,
+ server);
+ virEventAddHandle(vm->stderr,
+ POLLIN | POLLERR | POLLHUP,
+ qemudDispatchVMEvent,
+ server);
+
+ ret = 0;
+
+ if (qemudWaitForMonitor(server, vm) < 0) {
+ qemudShutdownVMDaemon(server, vm);
+ ret = -1;
+ }
+ }
+
+ if (vm->tapfds) {
+ for (i = 0; vm->tapfds[i] != -1; i++) {
+ close(vm->tapfds[i]);
+ vm->tapfds[i] = -1;
+ }
+ free(vm->tapfds);
+ vm->tapfds = NULL;
+ vm->ntapfds = 0;
+ }
+
+ for (i = 0 ; argv[i] ; i++)
+ free(argv[i]);
+ free(argv);
+
+ return ret;
+}
+
+static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED,
+ struct qemud_vm *vm, int fd) {
+ char buf[4096];
+ if (vm->pid < 0)
+ return 0;
+
+ for (;;) {
+ int ret = read(fd, buf, sizeof(buf)-1);
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+ if (ret == 0) {
+ return 0;
+ }
+ buf[ret] = '\0';
+
+ retry:
+ if (write(vm->logfile, buf, ret) < 0) {
+ /* Log, but ignore failures to write logfile for VM */
+ if (errno == EINTR)
+ goto retry;
+ qemudLog(QEMUD_WARN, "Unable to log VM console data: %s",
+ strerror(errno));
+ }
+ }
+}
+
+
+int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
+ if (!qemudIsActiveVM(vm))
+ return 0;
+
+ qemudLog(QEMUD_INFO, "Shutting down VM '%s'", vm->def->name);
+
+ kill(vm->pid, SIGTERM);
+
+ qemudVMData(server, vm, vm->stdout);
+ qemudVMData(server, vm, vm->stderr);
+
+ virEventRemoveHandle(vm->stdout);
+ virEventRemoveHandle(vm->stderr);
+
+ if (close(vm->logfile) < 0)
+ qemudLog(QEMUD_WARN, "Unable to close logfile %d: %s", errno, strerror(errno));
+ close(vm->stdout);
+ close(vm->stderr);
+ if (vm->monitor != -1)
+ close(vm->monitor);
+ vm->logfile = -1;
+ vm->stdout = -1;
+ vm->stderr = -1;
+ vm->monitor = -1;
+
+ if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
+ kill(vm->pid, SIGKILL);
+ if (waitpid(vm->pid, NULL, 0) != vm->pid) {
+ qemudLog(QEMUD_WARN, "Got unexpected pid, damn");
+ }
+ }
+
+ vm->pid = -1;
+ vm->id = -1;
+ vm->state = QEMUD_STATE_STOPPED;
+
+ if (vm->newDef) {
+ qemudFreeVMDef(vm->def);
+ vm->def = vm->newDef;
+ vm->newDef = NULL;
+ }
+
+ server->nactivevms--;
+ server->ninactivevms++;
+
+ return 0;
+}
+
+static int qemudDispatchVMLog(struct qemud_server *server, struct qemud_vm *vm, int fd) {
+ if (qemudVMData(server, vm, fd) < 0)
+ if (qemudShutdownVMDaemon(server, vm) < 0)
+ return -1;
+ return 0;
+}
+
+static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm,
+ int fd ATTRIBUTE_UNUSED) {
+ if (qemudShutdownVMDaemon(server, vm) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+qemudBuildDnsmasqArgv(struct qemud_server *server,
+ struct qemud_network *network,
+ char ***argv) {
+ int i, len;
+ char buf[PATH_MAX];
+ struct qemud_dhcp_range_def *range;
+
+ len =
+ 1 + /* dnsmasq */
+ 1 + /* --keep-in-foreground */
+ 1 + /* --strict-order */
+ 1 + /* --bind-interfaces */
+ 2 + /* --pid-file "" */
+ 2 + /* --conf-file "" */
+ /*2 + *//* --interface virbr0 */
+ 2 + /* --except-interface lo */
+ 2 + /* --listen-address 10.0.0.1 */
+ 1 + /* --dhcp-leasefile=path */
+ (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */
+ 1; /* NULL */
+
+ if (!(*argv = calloc(len, sizeof(char *))))
+ goto no_memory;
+
+#define APPEND_ARG(v, n, s) do { \
+ if (!((v)[(n)] = strdup(s))) \
+ goto no_memory; \
+ } while (0)
+
+ i = 0;
+
+ APPEND_ARG(*argv, i++, "dnsmasq");
+
+ APPEND_ARG(*argv, i++, "--keep-in-foreground");
+ /*
+ * Needed to ensure dnsmasq uses same algorithm for processing
+ * multiple nameserver entries in /etc/resolv.conf as GLibC.
+ */
+ APPEND_ARG(*argv, i++, "--strict-order");
+ APPEND_ARG(*argv, i++, "--bind-interfaces");
+
+ APPEND_ARG(*argv, i++, "--pid-file");
+ APPEND_ARG(*argv, i++, "");
+
+ APPEND_ARG(*argv, i++, "--conf-file");
+ APPEND_ARG(*argv, i++, "");
+
+ /*
+ * XXX does not actually work, due to some kind of
+ * race condition setting up ipv6 addresses on the
+ * interface. A sleep(10) makes it work, but that's
+ * clearly not practical
+ *
+ * APPEND_ARG(*argv, i++, "--interface");
+ * APPEND_ARG(*argv, i++, network->def->bridge);
+ */
+ APPEND_ARG(*argv, i++, "--listen-address");
+ APPEND_ARG(*argv, i++, network->def->ipAddress);
+
+ APPEND_ARG(*argv, i++, "--except-interface");
+ APPEND_ARG(*argv, i++, "lo");
+
+ /*
+ * NB, dnsmasq command line arg bug means we need to
+ * use a single arg '--dhcp-leasefile=path' rather than
+ * two separate args in '--dhcp-leasefile path' style
+ */
+ snprintf(buf, sizeof(buf), "--dhcp-leasefile=%s/lib/libvirt/dhcp-%s.leases",
+ LOCAL_STATE_DIR, network->def->name);
+ APPEND_ARG(*argv, i++, buf);
+
+ range = network->def->ranges;
+ while (range) {
+ snprintf(buf, sizeof(buf), "%s,%s",
+ range->start, range->end);
+
+ APPEND_ARG(*argv, i++, "--dhcp-range");
+ APPEND_ARG(*argv, i++, buf);
+
+ range = range->next;
+ }
+
+#undef APPEND_ARG
+
+ return 0;
+
+ no_memory:
+ if (argv) {
+ for (i = 0; (*argv)[i]; i++)
+ free((*argv)[i]);
+ free(*argv);
+ }
+ qemudReportError(server, VIR_ERR_NO_MEMORY, "dnsmasq argv");
+ return -1;
+}
+
+
+static int
+dhcpStartDhcpDaemon(struct qemud_server *server,
+ struct qemud_network *network)
+{
+ char **argv;
+ int ret, i;
+
+ if (network->def->ipAddress[0] == '\0') {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot start dhcp daemon without IP address for server");
+ return -1;
+ }
+
+ argv = NULL;
+ if (qemudBuildDnsmasqArgv(server, network, &argv) < 0)
+ return -1;
+
+ ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL);
+
+ for (i = 0; argv[i]; i++)
+ free(argv[i]);
+ free(argv);
+
+ return ret;
+}
+
+static int
+qemudAddIptablesRules(struct qemud_server *server,
+ struct qemud_network *network) {
+ int err;
+
+ if (!server->iptables && !(server->iptables = iptablesContextNew())) {
+ qemudReportError(server, VIR_ERR_NO_MEMORY, "iptables support");
+ return 1;
+ }
+
+
+ /* allow DHCP requests through to dnsmasq */
+ if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 67))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err1;
+ }
+
+ if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 67))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err2;
+ }
+
+ /* allow DNS requests through to dnsmasq */
+ if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 53))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err3;
+ }
+
+ if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 53))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err4;
+ }
+
+
+ /* Catch all rules to block forwarding to/from bridges */
+
+ if ((err = iptablesAddForwardRejectOut(server->iptables, network->bridge))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to block outbound traffic from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err5;
+ }
+
+ if ((err = iptablesAddForwardRejectIn(server->iptables, network->bridge))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to block inbound traffic to '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err6;
+ }
+
+ /* Allow traffic between guests on the same bridge */
+ if ((err = iptablesAddForwardAllowCross(server->iptables, network->bridge))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow cross bridge traffic on '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err7;
+ }
+
+
+ /* The remaining rules are only needed for IP forwarding */
+ if (!network->def->forward)
+ return 1;
+
+ /* allow forwarding packets from the bridge interface */
+ if ((err = iptablesAddForwardAllowOut(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow forwarding from '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err8;
+ }
+
+ /* allow forwarding packets to the bridge interface if they are part of an existing connection */
+ if ((err = iptablesAddForwardAllowIn(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to allow forwarding to '%s' : %s\n",
+ network->bridge, strerror(err));
+ goto err9;
+ }
+
+ /* enable masquerading */
+ if ((err = iptablesAddForwardMasquerade(server->iptables,
+ network->def->network,
+ network->def->forwardDev))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to add iptables rule to enable masquerading : %s\n",
+ strerror(err));
+ goto err10;
+ }
+
+ return 1;
+
+ err10:
+ iptablesRemoveForwardAllowIn(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev);
+ err9:
+ iptablesRemoveForwardAllowOut(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev);
+ err8:
+ iptablesRemoveForwardAllowCross(server->iptables,
+ network->bridge);
+ err7:
+ iptablesRemoveForwardRejectIn(server->iptables,
+ network->bridge);
+ err6:
+ iptablesRemoveForwardRejectOut(server->iptables,
+ network->bridge);
+ err5:
+ iptablesRemoveUdpInput(server->iptables, network->bridge, 53);
+ err4:
+ iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
+ err3:
+ iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
+ err2:
+ iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
+ err1:
+ return 0;
+}
+
+static void
+qemudRemoveIptablesRules(struct qemud_server *server,
+ struct qemud_network *network) {
+ if (network->def->forward) {
+ iptablesRemoveForwardMasquerade(server->iptables,
+ network->def->network,
+ network->def->forwardDev);
+ iptablesRemoveForwardAllowIn(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev);
+ iptablesRemoveForwardAllowOut(server->iptables,
+ network->def->network,
+ network->bridge,
+ network->def->forwardDev);
+ }
+ iptablesRemoveForwardAllowCross(server->iptables, network->bridge);
+ iptablesRemoveForwardRejectIn(server->iptables, network->bridge);
+ iptablesRemoveForwardRejectOut(server->iptables, network->bridge);
+ iptablesRemoveUdpInput(server->iptables, network->bridge, 53);
+ iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
+ iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
+ iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
+}
+
+static int
+qemudEnableIpForwarding(void)
+{
+#define PROC_IP_FORWARD "/proc/sys/net/ipv4/ip_forward"
+
+ int fd, ret;
+
+ if ((fd = open(PROC_IP_FORWARD, O_WRONLY|O_TRUNC)) == -1)
+ return 0;
+
+ if (write(fd, "1\n", 2) < 0)
+ ret = 0;
+
+ close (fd);
+
+ return 1;
+
+#undef PROC_IP_FORWARD
+}
+
+int qemudStartNetworkDaemon(struct qemud_server *server,
+ struct qemud_network *network) {
+ const char *name;
+ int err;
+
+ if (qemudIsActiveNetwork(network)) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "network is already active");
+ return -1;
+ }
+
+ if (!server->brctl && (err = brInit(&server->brctl))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot initialize bridge support: %s", strerror(err));
+ return -1;
+ }
+
+ if (network->def->bridge[0] == '\0' ||
+ strchr(network->def->bridge, '%')) {
+ name = "vnet%d";
+ } else {
+ name = network->def->bridge;
+ }
+
+ if ((err = brAddBridge(server->brctl, name, network->bridge, sizeof(network->bridge)))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot create bridge '%s' : %s", name, strerror(err));
+ return -1;
+ }
+
+ if (network->def->ipAddress[0] &&
+ (err = brSetInetAddress(server->brctl, network->bridge, network->def->ipAddress))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot set IP address on bridge '%s' to '%s' : %s\n",
+ network->bridge, network->def->ipAddress, strerror(err));
+ goto err_delbr;
+ }
+
+ if (network->def->netmask[0] &&
+ (err = brSetInetNetmask(server->brctl, network->bridge, network->def->netmask))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "cannot set netmask on bridge '%s' to '%s' : %s\n",
+ network->bridge, network->def->netmask, strerror(err));
+ goto err_delbr;
+ }
+
+ if (network->def->ipAddress[0] &&
+ (err = brSetInterfaceUp(server->brctl, network->bridge, 1))) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to bring the bridge '%s' up : %s\n",
+ network->bridge, strerror(err));
+ goto err_delbr;
+ }
+
+ if (!qemudAddIptablesRules(server, network))
+ goto err_delbr1;
+
+ if (network->def->forward &&
+ !qemudEnableIpForwarding()) {
+ qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+ "failed to enable IP forwarding : %s\n", strerror(err));
+ goto err_delbr2;
+ }
+
+ if (network->def->ranges &&
+ dhcpStartDhcpDaemon(server, network) < 0)
+ goto err_delbr2;
+
+ network->active = 1;
+
+ server->ninactivenetworks--;
+ server->nactivenetworks++;
+
+ return 0;
+
+ err_delbr2:
+ qemudRemoveIptablesRules(server, network);
+
+ err_delbr1:
+ if (network->def->ipAddress[0] &&
+ (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
+ qemudLog(QEMUD_WARN, "Failed to bring down bridge '%s' : %s",
+ network->bridge, strerror(err));
+ }
+
+ err_delbr:
+ if ((err = brDeleteBridge(server->brctl, network->bridge))) {
+ qemudLog(QEMUD_WARN, "Failed to delete bridge '%s' : %s\n",
+ network->bridge, strerror(err));
+ }
+
+ return -1;
+}
+
+
+int qemudShutdownNetworkDaemon(struct qemud_server *server,
+ struct qemud_network *network) {
+ int err;
+
+ qemudLog(QEMUD_INFO, "Shutting down network '%s'", network->def->name);
+
+ if (!qemudIsActiveNetwork(network))
+ return 0;
+
+ if (network->dnsmasqPid > 0)
+ kill(network->dnsmasqPid, SIGTERM);
+
+ qemudRemoveIptablesRules(server, network);
+
+ if (network->def->ipAddress[0] &&
+ (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
+ qemudLog(QEMUD_WARN, "Failed to bring down bridge '%s' : %s\n",
+ network->bridge, strerror(err));
+ }
+
+ if ((err = brDeleteBridge(server->brctl, network->bridge))) {
+ qemudLog(QEMUD_WARN, "Failed to delete bridge '%s' : %s\n",
+ network->bridge, strerror(err));
+ }
+
+ if (network->dnsmasqPid > 0 &&
+ waitpid(network->dnsmasqPid, NULL, WNOHANG) != network->dnsmasqPid) {
+ kill(network->dnsmasqPid, SIGKILL);
+ if (waitpid(network->dnsmasqPid, NULL, 0) != network->dnsmasqPid)
+ qemudLog(QEMUD_WARN, "Got unexpected pid for dnsmasq\n");
+ }
+
+ network->bridge[0] = '\0';
+ network->dnsmasqPid = -1;
+ network->active = 0;
+
+ if (network->newDef) {
+ qemudFreeNetworkDef(network->def);
+ network->def = network->newDef;
+ network->newDef = NULL;
+ }
+
+ server->nactivenetworks--;
+ server->ninactivenetworks++;
+
+ return 0;
+}
+
+
+static void qemudDispatchVMEvent(int fd, int events, void *opaque) {
+ struct qemud_server *server = (struct qemud_server *)opaque;
+ struct qemud_vm *vm = server->vms;
+
+ while (vm) {
+ if (qemudIsActiveVM(vm) &&
+ (vm->stdout == fd ||
+ vm->stderr == fd))
+ break;
+
+ vm = vm->next;
+ }
+
+ if (!vm)
+ return;
+
+ if (events == POLLIN &&
+ qemudDispatchVMLog(server, vm, fd) == 0)
+ return;
+
+ qemudDispatchVMFailure(server, vm, fd);
+}
+
int qemudMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED,
struct qemud_vm *vm,
const char *cmd,
#include "internal.h"
+int qemudStartVMDaemon(struct qemud_server *server,
+ struct qemud_vm *vm);
+
+int qemudShutdownVMDaemon(struct qemud_server *server,
+ struct qemud_vm *vm);
+
+int qemudStartNetworkDaemon(struct qemud_server *server,
+ struct qemud_network *network);
+
+int qemudShutdownNetworkDaemon(struct qemud_server *server,
+ struct qemud_network *network);
+
+void qemudShutdown(struct qemud_server *server);
+
void qemudReportError(struct qemud_server *server,
int code, const char *fmt, ...)
ATTRIBUTE_FORMAT(printf,3,4);
unsigned int shutdown : 1;
};
-int qemudStartVMDaemon(struct qemud_server *server,
- struct qemud_vm *vm);
-
-int qemudShutdownVMDaemon(struct qemud_server *server,
- struct qemud_vm *vm);
-
-int qemudStartNetworkDaemon(struct qemud_server *server,
- struct qemud_network *network);
-
-int qemudShutdownNetworkDaemon(struct qemud_server *server,
- struct qemud_network *network);
-
void qemudLog(int priority, const char *fmt, ...)
ATTRIBUTE_FORMAT(printf,2,3);
#include <string.h>
#include <errno.h>
#include <getopt.h>
-#include <ctype.h>
#include <assert.h>
#include <fnmatch.h>
#include <gnutls/gnutls.h>
#include "../src/remote_internal.h"
#include "../src/conf.h"
#include "dispatch.h"
-#include "driver.h"
#include "conf.h"
#include "iptables.h"
+#include "driver.h"
#include "event.h"
static int godaemon = 0; /* -d: Be a daemon */
errno = origerrno;
}
-static void qemudDispatchVMEvent(int fd, int events, void *opaque);
static void qemudDispatchClientEvent(int fd, int events, void *opaque);
static void qemudDispatchServerEvent(int fd, int events, void *opaque);
static int qemudRegisterClientEvent(struct qemud_server *server,
void *opaque) {
struct qemud_server *server = (struct qemud_server *)opaque;
unsigned char sigc;
- struct qemud_vm *vm;
- struct qemud_network *network;
int ret;
if (read(server->sigread, &sigc, 1) != 1) {
qemudLog(QEMUD_WARN, "Shutting down on signal %d", sigc);
if (!remote) {
- /* shutdown active VMs */
- vm = server->vms;
- while (vm) {
- struct qemud_vm *next = vm->next;
- if (qemudIsActiveVM(vm))
- qemudShutdownVMDaemon(server, vm);
- vm = next;
- }
-
- /* free inactive VMs */
- vm = server->vms;
- while (vm) {
- struct qemud_vm *next = vm->next;
- qemudFreeVM(vm);
- vm = next;
- }
- server->vms = NULL;
- server->nactivevms = 0;
- server->ninactivevms = 0;
-
- /* shutdown active networks */
- network = server->networks;
- while (network) {
- struct qemud_network *next = network->next;
- if (qemudIsActiveNetwork(network))
- qemudShutdownNetworkDaemon(server, network);
- network = next;
- }
-
- /* free inactive networks */
- network = server->networks;
- while (network) {
- struct qemud_network *next = network->next;
- qemudFreeNetwork(network);
- network = next;
- }
- server->networks = NULL;
- server->nactivenetworks = 0;
- server->ninactivenetworks = 0;
+ qemudShutdown(server);
}
server->shutdown = 1;
}
-static int
-qemudExec(struct qemud_server *server, char **argv,
- int *retpid, int *outfd, int *errfd) {
- int pid, null;
- int pipeout[2] = {-1,-1};
- int pipeerr[2] = {-1,-1};
-
- if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open %s : %s",
- _PATH_DEVNULL, strerror(errno));
- goto cleanup;
- }
-
- if ((outfd != NULL && pipe(pipeout) < 0) ||
- (errfd != NULL && pipe(pipeerr) < 0)) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe : %s",
- strerror(errno));
- goto cleanup;
- }
-
- if ((pid = fork()) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot fork child process : %s",
- strerror(errno));
- goto cleanup;
- }
-
- if (pid) { /* parent */
- close(null);
- if (outfd) {
- close(pipeout[1]);
- qemudSetNonBlock(pipeout[0]);
- qemudSetCloseExec(pipeout[0]);
- *outfd = pipeout[0];
- }
- if (errfd) {
- close(pipeerr[1]);
- qemudSetNonBlock(pipeerr[0]);
- qemudSetCloseExec(pipeerr[0]);
- *errfd = pipeerr[0];
- }
- *retpid = pid;
- return 0;
- }
-
- /* child */
-
- if (pipeout[0] > 0 && close(pipeout[0]) < 0)
- _exit(1);
- if (pipeerr[0] > 0 && close(pipeerr[0]) < 0)
- _exit(1);
-
- if (dup2(null, STDIN_FILENO) < 0)
- _exit(1);
- if (dup2(pipeout[1] > 0 ? pipeout[1] : null, STDOUT_FILENO) < 0)
- _exit(1);
- if (dup2(pipeerr[1] > 0 ? pipeerr[1] : null, STDERR_FILENO) < 0)
- _exit(1);
-
- close(null);
- if (pipeout[1] > 0)
- close(pipeout[1]);
- if (pipeerr[1] > 0)
- close(pipeerr[1]);
-
- execvp(argv[0], argv);
-
- _exit(1);
-
- return 0;
-
- cleanup:
- if (pipeerr[0] > 0)
- close(pipeerr[0]);
- if (pipeerr[1] > 0)
- close(pipeerr[1]);
- if (pipeout[0] > 0)
- close(pipeout[0]);
- if (pipeout[1] > 0)
- close(pipeout[1]);
- if (null > 0)
- close(null);
- return -1;
-}
-
-/* Return -1 for error, 1 to continue reading and 0 for success */
-typedef int qemudHandlerMonitorOutput(struct qemud_server *server,
- struct qemud_vm *vm,
- const char *output,
- int fd);
-
-static int
-qemudReadMonitorOutput(struct qemud_server *server,
- struct qemud_vm *vm,
- int fd,
- char *buffer,
- int buflen,
- qemudHandlerMonitorOutput func,
- const char *what)
-{
-#define MONITOR_TIMEOUT 3000
-
- int got = 0;
- buffer[0] = '\0';
-
- /* Consume & discard the initial greeting */
- while (got < (buflen-1)) {
- int ret;
-
- ret = read(fd, buffer+got, buflen-got-1);
- if (ret == 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "QEMU quit during %s startup\n%s", what, buffer);
- return -1;
- }
- if (ret < 0) {
- struct pollfd pfd = { .fd = fd, .events = POLLIN };
- if (errno == EINTR)
- continue;
-
- if (errno != EAGAIN) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Failure while reading %s startup output: %s",
- what, strerror(errno));
- return -1;
- }
-
- ret = poll(&pfd, 1, MONITOR_TIMEOUT);
- if (ret == 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Timed out while reading %s startup output", what);
- return -1;
- } else if (ret == -1) {
- if (errno != EINTR) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Failure while reading %s startup output: %s",
- what, strerror(errno));
- return -1;
- }
- } else {
- /* Make sure we continue loop & read any further data
- available before dealing with EOF */
- if (pfd.revents & (POLLIN | POLLHUP))
- continue;
-
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Failure while reading %s startup output", what);
- return -1;
- }
- } else {
- got += ret;
- buffer[got] = '\0';
- if ((ret = func(server, vm, buffer, fd)) != 1)
- return ret;
- }
- }
-
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Out of space while reading %s startup output", what);
- return -1;
-
-#undef MONITOR_TIMEOUT
-}
-
-static int
-qemudCheckMonitorPrompt(struct qemud_server *server ATTRIBUTE_UNUSED,
- struct qemud_vm *vm,
- const char *output,
- int fd)
-{
- if (strstr(output, "(qemu) ") == NULL)
- return 1; /* keep reading */
-
- vm->monitor = fd;
-
- return 0;
-}
-
-static int qemudOpenMonitor(struct qemud_server *server, struct qemud_vm *vm, const char *monitor) {
- int monfd;
- char buffer[1024];
- int ret = -1;
-
- if (!(monfd = open(monitor, O_RDWR))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Unable to open monitor path %s", monitor);
- return -1;
- }
- if (qemudSetCloseExec(monfd) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Unable to set monitor close-on-exec flag");
- goto error;
- }
- if (qemudSetNonBlock(monfd) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Unable to put monitor into non-blocking mode");
- goto error;
- }
-
- ret = qemudReadMonitorOutput(server, vm, monfd,
- buffer, sizeof(buffer),
- qemudCheckMonitorPrompt,
- "monitor");
- error:
- close(monfd);
- return ret;
-}
-
-static int qemudExtractMonitorPath(const char *haystack, char *path, int pathmax) {
- static const char needle[] = "char device redirected to";
- char *tmp;
-
- if (!(tmp = strstr(haystack, needle)))
- return -1;
-
- strncpy(path, tmp+sizeof(needle), pathmax-1);
- path[pathmax-1] = '\0';
-
- while (*path) {
- /*
- * The monitor path ends at first whitespace char
- * so lets search for it & NULL terminate it there
- */
- if (isspace(*path)) {
- *path = '\0';
- return 0;
- }
- path++;
- }
-
- /*
- * We found a path, but didn't find any whitespace,
- * so it must be still incomplete - we should at
- * least see a \n
- */
- return -1;
-}
-
-static int
-qemudOpenMonitorPath(struct qemud_server *server,
- struct qemud_vm *vm,
- const char *output,
- int fd ATTRIBUTE_UNUSED)
-{
- char monitor[PATH_MAX];
-
- if (qemudExtractMonitorPath(output, monitor, sizeof(monitor)) < 0)
- return 1; /* keep reading */
-
- return qemudOpenMonitor(server, vm, monitor);
-}
-
-static int qemudWaitForMonitor(struct qemud_server *server, struct qemud_vm *vm) {
- char buffer[1024]; /* Plenty of space to get startup greeting */
- int ret = qemudReadMonitorOutput(server, vm, vm->stderr,
- buffer, sizeof(buffer),
- qemudOpenMonitorPath,
- "console");
-
- buffer[sizeof(buffer)-1] = '\0';
- retry:
- if (write(vm->logfile, buffer, strlen(buffer)) < 0) {
- /* Log, but ignore failures to write logfile for VM */
- if (errno == EINTR)
- goto retry;
- qemudLog(QEMUD_WARN, "Unable to log VM console data: %s",
- strerror(errno));
- }
-
- return ret;
-}
-
-static int qemudNextFreeVNCPort(struct qemud_server *server ATTRIBUTE_UNUSED) {
- int i;
-
- for (i = 5900 ; i < 6000 ; i++) {
- int fd;
- int reuse = 1;
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(i);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- fd = socket(PF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- return -1;
-
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) < 0) {
- close(fd);
- break;
- }
-
- if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
- /* Not in use, lets grab it */
- close(fd);
- return i;
- }
- close(fd);
-
- if (errno == EADDRINUSE) {
- /* In use, try next */
- continue;
- }
- /* Some other bad failure, get out.. */
- break;
- }
- return -1;
-}
-
-int qemudStartVMDaemon(struct qemud_server *server,
- struct qemud_vm *vm) {
- char **argv = NULL, **tmp;
- int i, ret = -1;
- char logfile[PATH_MAX];
-
- if (qemudIsActiveVM(vm)) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "VM is already active");
- return -1;
- }
-
- if (vm->def->vncPort < 0) {
- int port = qemudNextFreeVNCPort(server);
- if (port < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "Unable to find an unused VNC port");
- return -1;
- }
- vm->def->vncActivePort = port;
- } else
- vm->def->vncActivePort = vm->def->vncPort;
-
- if ((strlen(server->logDir) + /* path */
- 1 + /* Separator */
- strlen(vm->def->name) + /* basename */
- 4 + /* suffix .log */
- 1 /* NULL */) > PATH_MAX) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "config file path too long: %s/%s.log",
- server->logDir, vm->def->name);
- return -1;
- }
- strcpy(logfile, server->logDir);
- strcat(logfile, "/");
- strcat(logfile, vm->def->name);
- strcat(logfile, ".log");
-
- if (qemudEnsureDir(server->logDir) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot create log directory %s: %s",
- server->logDir, strerror(errno));
- return -1;
- }
-
- if ((vm->logfile = open(logfile, O_CREAT | O_TRUNC | O_WRONLY,
- S_IRUSR | S_IWUSR)) < 0) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to create logfile %s: %s",
- logfile, strerror(errno));
- return -1;
- }
-
- if (qemudBuildCommandLine(server, vm, &argv) < 0) {
- close(vm->logfile);
- vm->logfile = -1;
- return -1;
- }
-
- tmp = argv;
- while (*tmp) {
- if (write(vm->logfile, *tmp, strlen(*tmp)) < 0)
- qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
- errno, strerror(errno));
- if (write(vm->logfile, " ", 1) < 0)
- qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
- errno, strerror(errno));
- tmp++;
- }
- if (write(vm->logfile, "\n", 1) < 0)
- qemudLog(QEMUD_WARN, "Unable to write argv to logfile %d: %s",
- errno, strerror(errno));
-
- if (qemudExec(server, argv, &vm->pid, &vm->stdout, &vm->stderr) == 0) {
- vm->id = server->nextvmid++;
- vm->state = QEMUD_STATE_RUNNING;
-
- server->ninactivevms--;
- server->nactivevms++;
-
- virEventAddHandle(vm->stdout,
- POLLIN | POLLERR | POLLHUP,
- qemudDispatchVMEvent,
- server);
- virEventAddHandle(vm->stderr,
- POLLIN | POLLERR | POLLHUP,
- qemudDispatchVMEvent,
- server);
-
- ret = 0;
-
- if (qemudWaitForMonitor(server, vm) < 0) {
- qemudShutdownVMDaemon(server, vm);
- ret = -1;
- }
- }
-
- if (vm->tapfds) {
- for (i = 0; vm->tapfds[i] != -1; i++) {
- close(vm->tapfds[i]);
- vm->tapfds[i] = -1;
- }
- free(vm->tapfds);
- vm->tapfds = NULL;
- vm->ntapfds = 0;
- }
-
- for (i = 0 ; argv[i] ; i++)
- free(argv[i]);
- free(argv);
-
- return ret;
-}
static void qemudDispatchClientFailure(struct qemud_server *server, struct qemud_client *client) {
}
}
-static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED,
- struct qemud_vm *vm, int fd) {
- char buf[4096];
- if (vm->pid < 0)
- return 0;
-
- for (;;) {
- int ret = read(fd, buf, sizeof(buf)-1);
- if (ret < 0) {
- if (errno == EAGAIN)
- return 0;
- return -1;
- }
- if (ret == 0) {
- return 0;
- }
- buf[ret] = '\0';
-
- retry:
- if (write(vm->logfile, buf, ret) < 0) {
- /* Log, but ignore failures to write logfile for VM */
- if (errno == EINTR)
- goto retry;
- qemudLog(QEMUD_WARN, "Unable to log VM console data: %s",
- strerror(errno));
- }
- }
-}
-
-
-int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
- if (!qemudIsActiveVM(vm))
- return 0;
-
- qemudLog(QEMUD_INFO, "Shutting down VM '%s'", vm->def->name);
-
- kill(vm->pid, SIGTERM);
-
- qemudVMData(server, vm, vm->stdout);
- qemudVMData(server, vm, vm->stderr);
-
- virEventRemoveHandle(vm->stdout);
- virEventRemoveHandle(vm->stderr);
-
- if (close(vm->logfile) < 0)
- qemudLog(QEMUD_WARN, "Unable to close logfile %d: %s", errno, strerror(errno));
- close(vm->stdout);
- close(vm->stderr);
- if (vm->monitor != -1)
- close(vm->monitor);
- vm->logfile = -1;
- vm->stdout = -1;
- vm->stderr = -1;
- vm->monitor = -1;
-
- if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
- kill(vm->pid, SIGKILL);
- if (waitpid(vm->pid, NULL, 0) != vm->pid) {
- qemudLog(QEMUD_WARN, "Got unexpected pid, damn");
- }
- }
-
- vm->pid = -1;
- vm->id = -1;
- vm->state = QEMUD_STATE_STOPPED;
-
- if (vm->newDef) {
- qemudFreeVMDef(vm->def);
- vm->def = vm->newDef;
- vm->newDef = NULL;
- }
-
- server->nactivevms--;
- server->ninactivevms++;
-
- return 0;
-}
-
-static int qemudDispatchVMLog(struct qemud_server *server, struct qemud_vm *vm, int fd) {
- if (qemudVMData(server, vm, fd) < 0)
- if (qemudShutdownVMDaemon(server, vm) < 0)
- return -1;
- return 0;
-}
-
-static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm,
- int fd ATTRIBUTE_UNUSED) {
- if (qemudShutdownVMDaemon(server, vm) < 0)
- return -1;
- return 0;
-}
-
-static int
-qemudBuildDnsmasqArgv(struct qemud_server *server,
- struct qemud_network *network,
- char ***argv) {
- int i, len;
- char buf[PATH_MAX];
- struct qemud_dhcp_range_def *range;
-
- len =
- 1 + /* dnsmasq */
- 1 + /* --keep-in-foreground */
- 1 + /* --strict-order */
- 1 + /* --bind-interfaces */
- 2 + /* --pid-file "" */
- 2 + /* --conf-file "" */
- /*2 + *//* --interface virbr0 */
- 2 + /* --except-interface lo */
- 2 + /* --listen-address 10.0.0.1 */
- 1 + /* --dhcp-leasefile=path */
- (2 * network->def->nranges) + /* --dhcp-range 10.0.0.2,10.0.0.254 */
- 1; /* NULL */
-
- if (!(*argv = calloc(len, sizeof(char *))))
- goto no_memory;
-
-#define APPEND_ARG(v, n, s) do { \
- if (!((v)[(n)] = strdup(s))) \
- goto no_memory; \
- } while (0)
-
- i = 0;
-
- APPEND_ARG(*argv, i++, "dnsmasq");
-
- APPEND_ARG(*argv, i++, "--keep-in-foreground");
- /*
- * Needed to ensure dnsmasq uses same algorithm for processing
- * multiple nameserver entries in /etc/resolv.conf as GLibC.
- */
- APPEND_ARG(*argv, i++, "--strict-order");
- APPEND_ARG(*argv, i++, "--bind-interfaces");
-
- APPEND_ARG(*argv, i++, "--pid-file");
- APPEND_ARG(*argv, i++, "");
-
- APPEND_ARG(*argv, i++, "--conf-file");
- APPEND_ARG(*argv, i++, "");
-
- /*
- * XXX does not actually work, due to some kind of
- * race condition setting up ipv6 addresses on the
- * interface. A sleep(10) makes it work, but that's
- * clearly not practical
- *
- * APPEND_ARG(*argv, i++, "--interface");
- * APPEND_ARG(*argv, i++, network->def->bridge);
- */
- APPEND_ARG(*argv, i++, "--listen-address");
- APPEND_ARG(*argv, i++, network->def->ipAddress);
-
- APPEND_ARG(*argv, i++, "--except-interface");
- APPEND_ARG(*argv, i++, "lo");
-
- /*
- * NB, dnsmasq command line arg bug means we need to
- * use a single arg '--dhcp-leasefile=path' rather than
- * two separate args in '--dhcp-leasefile path' style
- */
- snprintf(buf, sizeof(buf), "--dhcp-leasefile=%s/lib/libvirt/dhcp-%s.leases",
- LOCAL_STATE_DIR, network->def->name);
- APPEND_ARG(*argv, i++, buf);
-
- range = network->def->ranges;
- while (range) {
- snprintf(buf, sizeof(buf), "%s,%s",
- range->start, range->end);
-
- APPEND_ARG(*argv, i++, "--dhcp-range");
- APPEND_ARG(*argv, i++, buf);
-
- range = range->next;
- }
-
-#undef APPEND_ARG
-
- return 0;
-
- no_memory:
- if (argv) {
- for (i = 0; (*argv)[i]; i++)
- free((*argv)[i]);
- free(*argv);
- }
- qemudReportError(server, VIR_ERR_NO_MEMORY, "dnsmasq argv");
- return -1;
-}
-
-
-static int
-dhcpStartDhcpDaemon(struct qemud_server *server,
- struct qemud_network *network)
-{
- char **argv;
- int ret, i;
-
- if (network->def->ipAddress[0] == '\0') {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot start dhcp daemon without IP address for server");
- return -1;
- }
-
- argv = NULL;
- if (qemudBuildDnsmasqArgv(server, network, &argv) < 0)
- return -1;
-
- ret = qemudExec(server, argv, &network->dnsmasqPid, NULL, NULL);
-
- for (i = 0; argv[i]; i++)
- free(argv[i]);
- free(argv);
-
- return ret;
-}
-
-static int
-qemudAddIptablesRules(struct qemud_server *server,
- struct qemud_network *network) {
- int err;
-
- if (!server->iptables && !(server->iptables = iptablesContextNew())) {
- qemudReportError(server, VIR_ERR_NO_MEMORY, "iptables support");
- return 1;
- }
-
-
- /* allow DHCP requests through to dnsmasq */
- if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 67))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err1;
- }
-
- if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 67))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow DHCP requests from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err2;
- }
-
- /* allow DNS requests through to dnsmasq */
- if ((err = iptablesAddTcpInput(server->iptables, network->bridge, 53))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err3;
- }
-
- if ((err = iptablesAddUdpInput(server->iptables, network->bridge, 53))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow DNS requests from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err4;
- }
-
-
- /* Catch all rules to block forwarding to/from bridges */
-
- if ((err = iptablesAddForwardRejectOut(server->iptables, network->bridge))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to block outbound traffic from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err5;
- }
-
- if ((err = iptablesAddForwardRejectIn(server->iptables, network->bridge))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to block inbound traffic to '%s' : %s\n",
- network->bridge, strerror(err));
- goto err6;
- }
-
- /* Allow traffic between guests on the same bridge */
- if ((err = iptablesAddForwardAllowCross(server->iptables, network->bridge))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow cross bridge traffic on '%s' : %s\n",
- network->bridge, strerror(err));
- goto err7;
- }
-
-
- /* The remaining rules are only needed for IP forwarding */
- if (!network->def->forward)
- return 1;
-
- /* allow forwarding packets from the bridge interface */
- if ((err = iptablesAddForwardAllowOut(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow forwarding from '%s' : %s\n",
- network->bridge, strerror(err));
- goto err8;
- }
-
- /* allow forwarding packets to the bridge interface if they are part of an existing connection */
- if ((err = iptablesAddForwardAllowIn(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to allow forwarding to '%s' : %s\n",
- network->bridge, strerror(err));
- goto err9;
- }
-
- /* enable masquerading */
- if ((err = iptablesAddForwardMasquerade(server->iptables,
- network->def->network,
- network->def->forwardDev))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to add iptables rule to enable masquerading : %s\n",
- strerror(err));
- goto err10;
- }
-
- return 1;
-
- err10:
- iptablesRemoveForwardAllowIn(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev);
- err9:
- iptablesRemoveForwardAllowOut(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev);
- err8:
- iptablesRemoveForwardAllowCross(server->iptables,
- network->bridge);
- err7:
- iptablesRemoveForwardRejectIn(server->iptables,
- network->bridge);
- err6:
- iptablesRemoveForwardRejectOut(server->iptables,
- network->bridge);
- err5:
- iptablesRemoveUdpInput(server->iptables, network->bridge, 53);
- err4:
- iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
- err3:
- iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
- err2:
- iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
- err1:
- return 0;
-}
-
-static void
-qemudRemoveIptablesRules(struct qemud_server *server,
- struct qemud_network *network) {
- if (network->def->forward) {
- iptablesRemoveForwardMasquerade(server->iptables,
- network->def->network,
- network->def->forwardDev);
- iptablesRemoveForwardAllowIn(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev);
- iptablesRemoveForwardAllowOut(server->iptables,
- network->def->network,
- network->bridge,
- network->def->forwardDev);
- }
- iptablesRemoveForwardAllowCross(server->iptables, network->bridge);
- iptablesRemoveForwardRejectIn(server->iptables, network->bridge);
- iptablesRemoveForwardRejectOut(server->iptables, network->bridge);
- iptablesRemoveUdpInput(server->iptables, network->bridge, 53);
- iptablesRemoveTcpInput(server->iptables, network->bridge, 53);
- iptablesRemoveUdpInput(server->iptables, network->bridge, 67);
- iptablesRemoveTcpInput(server->iptables, network->bridge, 67);
-}
-
-static int
-qemudEnableIpForwarding(void)
-{
-#define PROC_IP_FORWARD "/proc/sys/net/ipv4/ip_forward"
-
- int fd, ret;
-
- if ((fd = open(PROC_IP_FORWARD, O_WRONLY|O_TRUNC)) == -1)
- return 0;
-
- if (write(fd, "1\n", 2) < 0)
- ret = 0;
-
- close (fd);
-
- return 1;
-
-#undef PROC_IP_FORWARD
-}
-
-int qemudStartNetworkDaemon(struct qemud_server *server,
- struct qemud_network *network) {
- const char *name;
- int err;
-
- if (qemudIsActiveNetwork(network)) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "network is already active");
- return -1;
- }
-
- if (!server->brctl && (err = brInit(&server->brctl))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot initialize bridge support: %s", strerror(err));
- return -1;
- }
-
- if (network->def->bridge[0] == '\0' ||
- strchr(network->def->bridge, '%')) {
- name = "vnet%d";
- } else {
- name = network->def->bridge;
- }
-
- if ((err = brAddBridge(server->brctl, name, network->bridge, sizeof(network->bridge)))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot create bridge '%s' : %s", name, strerror(err));
- return -1;
- }
-
- if (network->def->ipAddress[0] &&
- (err = brSetInetAddress(server->brctl, network->bridge, network->def->ipAddress))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot set IP address on bridge '%s' to '%s' : %s\n",
- network->bridge, network->def->ipAddress, strerror(err));
- goto err_delbr;
- }
-
- if (network->def->netmask[0] &&
- (err = brSetInetNetmask(server->brctl, network->bridge, network->def->netmask))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "cannot set netmask on bridge '%s' to '%s' : %s\n",
- network->bridge, network->def->netmask, strerror(err));
- goto err_delbr;
- }
-
- if (network->def->ipAddress[0] &&
- (err = brSetInterfaceUp(server->brctl, network->bridge, 1))) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to bring the bridge '%s' up : %s\n",
- network->bridge, strerror(err));
- goto err_delbr;
- }
-
- if (!qemudAddIptablesRules(server, network))
- goto err_delbr1;
-
- if (network->def->forward &&
- !qemudEnableIpForwarding()) {
- qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
- "failed to enable IP forwarding : %s\n", strerror(err));
- goto err_delbr2;
- }
-
- if (network->def->ranges &&
- dhcpStartDhcpDaemon(server, network) < 0)
- goto err_delbr2;
-
- network->active = 1;
-
- server->ninactivenetworks--;
- server->nactivenetworks++;
-
- return 0;
-
- err_delbr2:
- qemudRemoveIptablesRules(server, network);
-
- err_delbr1:
- if (network->def->ipAddress[0] &&
- (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
- qemudLog(QEMUD_WARN, "Failed to bring down bridge '%s' : %s",
- network->bridge, strerror(err));
- }
-
- err_delbr:
- if ((err = brDeleteBridge(server->brctl, network->bridge))) {
- qemudLog(QEMUD_WARN, "Failed to delete bridge '%s' : %s\n",
- network->bridge, strerror(err));
- }
-
- return -1;
-}
-
-
-int qemudShutdownNetworkDaemon(struct qemud_server *server,
- struct qemud_network *network) {
- int err;
-
- qemudLog(QEMUD_INFO, "Shutting down network '%s'", network->def->name);
-
- if (!qemudIsActiveNetwork(network))
- return 0;
-
- if (network->dnsmasqPid > 0)
- kill(network->dnsmasqPid, SIGTERM);
-
- qemudRemoveIptablesRules(server, network);
-
- if (network->def->ipAddress[0] &&
- (err = brSetInterfaceUp(server->brctl, network->bridge, 0))) {
- qemudLog(QEMUD_WARN, "Failed to bring down bridge '%s' : %s\n",
- network->bridge, strerror(err));
- }
-
- if ((err = brDeleteBridge(server->brctl, network->bridge))) {
- qemudLog(QEMUD_WARN, "Failed to delete bridge '%s' : %s\n",
- network->bridge, strerror(err));
- }
-
- if (network->dnsmasqPid > 0 &&
- waitpid(network->dnsmasqPid, NULL, WNOHANG) != network->dnsmasqPid) {
- kill(network->dnsmasqPid, SIGKILL);
- if (waitpid(network->dnsmasqPid, NULL, 0) != network->dnsmasqPid)
- qemudLog(QEMUD_WARN, "Got unexpected pid for dnsmasq\n");
- }
-
- network->bridge[0] = '\0';
- network->dnsmasqPid = -1;
- network->active = 0;
-
- if (network->newDef) {
- qemudFreeNetworkDef(network->def);
- network->def = network->newDef;
- network->newDef = NULL;
- }
-
- server->nactivenetworks--;
- server->ninactivenetworks++;
-
- return 0;
-}
-
-
-static void qemudDispatchVMEvent(int fd, int events, void *opaque) {
- struct qemud_server *server = (struct qemud_server *)opaque;
- struct qemud_vm *vm = server->vms;
-
- while (vm) {
- if (qemudIsActiveVM(vm) &&
- (vm->stdout == fd ||
- vm->stderr == fd))
- break;
-
- vm = vm->next;
- }
-
- if (!vm)
- return;
-
- if (events == POLLIN &&
- qemudDispatchVMLog(server, vm, fd) == 0)
- return;
-
- qemudDispatchVMFailure(server, vm, fd);
-}
static void qemudDispatchClientEvent(int fd, int events, void *opaque) {
struct qemud_server *server = (struct qemud_server *)opaque;