The main qemud.c file has alot of code which deals with starting and stopping QEMU, dnsmasq processes, setting up iptables and generally controlling the lifecycle of VMs and networks. With the advent of the general purpose event loop code, there is no need for any of this to be present in the qemud.c file. So this patch moves the code into the driver.c file. The qemud.d file is now solely responsible with client/server socket code. driver.c | 1041 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ driver.h | 14 internal.h | 14 qemud.c | 1036 ------------------------------------------------------------ 4 files changed, 1062 insertions(+), 1043 deletions(-) Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
diff -r a0bee41d0bd9 qemud/driver.c --- a/qemud/driver.c Sun Jun 17 17:37:33 2007 -0400 +++ b/qemud/driver.c Sun Jun 17 17:37:40 2007 -0400 @@ -35,9 +35,16 @@ #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" @@ -54,6 +61,1040 @@ void qemudReportError(struct qemud_serve } else { server->errorMessage[0] = '\0'; } +} + +static void qemudDispatchVMEvent(int fd, int events, void *opaque); + +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++; + server->nvmfds += 2; + + 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; + server->nvmfds -= 2; + + 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, diff -r a0bee41d0bd9 qemud/driver.h --- a/qemud/driver.h Sun Jun 17 17:37:33 2007 -0400 +++ b/qemud/driver.h Sun Jun 17 17:37:40 2007 -0400 @@ -26,6 +26,20 @@ #define QEMUD_DRIVER_H #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, ...) diff -r a0bee41d0bd9 qemud/internal.h --- a/qemud/internal.h Sun Jun 17 17:37:33 2007 -0400 +++ b/qemud/internal.h Sun Jun 17 17:37:40 2007 -0400 @@ -358,17 +358,9 @@ struct qemud_server { 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); +int qemudSetNonBlock(int fd); +int qemudSetCloseExec(int fd); + void qemudLog(int priority, const char *fmt, ...) ATTRIBUTE_FORMAT(printf,2,3); diff -r a0bee41d0bd9 qemud/qemud.c --- a/qemud/qemud.c Sun Jun 17 17:37:33 2007 -0400 +++ b/qemud/qemud.c Sun Jun 17 17:37:40 2007 -0400 @@ -46,7 +46,6 @@ #include <string.h> #include <errno.h> #include <getopt.h> -#include <ctype.h> #include <assert.h> #include <fnmatch.h> #include <gnutls/gnutls.h> @@ -58,9 +57,9 @@ #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 */ @@ -111,7 +110,6 @@ static void sig_handler(int sig) { 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, @@ -197,8 +195,6 @@ static void qemudDispatchSignalEvent(int 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) { @@ -228,45 +224,7 @@ static void qemudDispatchSignalEvent(int 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; @@ -280,7 +238,7 @@ static void qemudDispatchSignalEvent(int server->shutdown = 1; } -static int qemudSetCloseExec(int fd) { +int qemudSetCloseExec(int fd) { int flags; if ((flags = fcntl(fd, F_GETFD)) < 0) goto error; @@ -294,7 +252,7 @@ static int qemudSetCloseExec(int fd) { } -static int qemudSetNonBlock(int fd) { +int qemudSetNonBlock(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL)) < 0) goto error; @@ -1112,428 +1070,6 @@ static int qemudDispatchServer(struct qe } -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++; - server->nvmfds += 2; - - 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) { struct qemud_client *tmp = server->clients; @@ -1886,570 +1422,6 @@ static void qemudDispatchClientWrite(str } } -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; - server->nvmfds -= 2; - - 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;