diff -r a04dbba5004f qemud/driver.c --- a/qemud/driver.c Thu Jun 21 12:06:05 2007 -0400 +++ b/qemud/driver.c Thu Jun 21 16:07:18 2007 -0400 @@ -35,9 +35,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include +#include "event.h" #include "buf.h" #include "internal.h" #include "driver.h" @@ -54,6 +61,1066 @@ void qemudReportError(struct qemud_serve } else { server->errorMessage[0] = '\0'; } +} + +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, diff -r a04dbba5004f qemud/driver.h --- a/qemud/driver.h Thu Jun 21 12:06:05 2007 -0400 +++ b/qemud/driver.h Thu Jun 21 16:06:14 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 a04dbba5004f qemud/internal.h --- a/qemud/internal.h Thu Jun 21 12:06:05 2007 -0400 +++ b/qemud/internal.h Thu Jun 21 16:06:53 2007 -0400 @@ -357,18 +357,6 @@ 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); - void qemudLog(int priority, const char *fmt, ...) ATTRIBUTE_FORMAT(printf,2,3); diff -r a04dbba5004f qemud/qemud.c --- a/qemud/qemud.c Thu Jun 21 12:06:05 2007 -0400 +++ b/qemud/qemud.c Thu Jun 21 16:07:07 2007 -0400 @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -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; @@ -1112,426 +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++; - - 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) { @@ -1885,569 +1423,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; - - 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;