DAEMON_SOURCES = \
event.c event.h \
- qemud.c qemud.h \
+ libvirtd.c libvirtd.h \
remote.c remote.h \
dispatch.c dispatch.h \
remote_dispatch_prototypes.h \
#define __LIBVIRTD_DISPATCH_H__
-#include "qemud.h"
+#include "libvirtd.h"
int
--- /dev/null
+/*
+ * libvirtd.c: daemon start of day, guest process & i/o management
+ *
+ * Copyright (C) 2006, 2007, 2008, 2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fnmatch.h>
+#include <grp.h>
+#include <signal.h>
+#include <netdb.h>
+
+#include "libvirt_internal.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+#include "libvirtd.h"
+#include "dispatch.h"
+
+#include "util.h"
+#include "remote/remote_driver.h"
+#include "conf.h"
+#include "event.h"
+#include "memory.h"
+#ifdef HAVE_AVAHI
+#include "mdns.h"
+#endif
+
+#ifdef WITH_DRIVER_MODULES
+#include "driver.h"
+#else
+#ifdef WITH_QEMU
+#include "qemu/qemu_driver.h"
+#endif
+#ifdef WITH_LXC
+#include "lxc/lxc_driver.h"
+#endif
+#ifdef WITH_UML
+#include "uml/uml_driver.h"
+#endif
+#ifdef WITH_ONE
+#include "opennebula/one_driver.h"
+#endif
+#ifdef WITH_NETWORK
+#include "network/bridge_driver.h"
+#endif
+#ifdef WITH_NETCF
+#include "interface/netcf_driver.h"
+#endif
+#ifdef WITH_STORAGE_DIR
+#include "storage/storage_driver.h"
+#endif
+#ifdef WITH_NODE_DEVICES
+#include "node_device/node_device_driver.h"
+#endif
+#include "secret/secret_driver.h"
+#endif
+
+
+#ifdef __sun
+#include <ucred.h>
+#include <priv.h>
+
+#ifndef PRIV_VIRT_MANAGE
+#define PRIV_VIRT_MANAGE ((const char *)"virt_manage")
+#endif
+
+#ifndef PRIV_XVM_CONTROL
+#define PRIV_XVM_CONTROL ((const char *)"xvm_control")
+#endif
+
+#define PU_RESETGROUPS 0x0001 /* Remove supplemental groups */
+#define PU_CLEARLIMITSET 0x0008 /* L=0 */
+
+extern int __init_daemon_priv(int, uid_t, gid_t, ...);
+
+#define SYSTEM_UID 60
+
+static gid_t unix_sock_gid = 60; /* Not used */
+static int unix_sock_rw_mask = 0666;
+static int unix_sock_ro_mask = 0666;
+
+#else
+
+static gid_t unix_sock_gid = 0; /* Only root by default */
+static int unix_sock_rw_mask = 0700; /* Allow user only */
+static int unix_sock_ro_mask = 0777; /* Allow world */
+
+#endif /* __sun */
+
+static int godaemon = 0; /* -d: Be a daemon */
+static int verbose = 0; /* -v: Verbose mode */
+static int timeout = -1; /* -t: Shutdown timeout */
+static int sigwrite = -1; /* Signal handler pipe */
+static int ipsock = 0; /* -l Listen for TCP/IP */
+
+/* Defaults for configuration file elements */
+static int listen_tls = 1;
+static int listen_tcp = 0;
+static char *listen_addr = (char *) LIBVIRTD_LISTEN_ADDR;
+static char *tls_port = (char *) LIBVIRTD_TLS_PORT;
+static char *tcp_port = (char *) LIBVIRTD_TCP_PORT;
+
+static char *unix_sock_dir = NULL;
+
+#if HAVE_POLKIT
+static int auth_unix_rw = REMOTE_AUTH_POLKIT;
+static int auth_unix_ro = REMOTE_AUTH_POLKIT;
+#else
+static int auth_unix_rw = REMOTE_AUTH_NONE;
+static int auth_unix_ro = REMOTE_AUTH_NONE;
+#endif /* HAVE_POLKIT */
+#if HAVE_SASL
+static int auth_tcp = REMOTE_AUTH_SASL;
+#else
+static int auth_tcp = REMOTE_AUTH_NONE;
+#endif
+static int auth_tls = REMOTE_AUTH_NONE;
+
+static int mdns_adv = 1;
+static char *mdns_name = NULL;
+
+static int tls_no_verify_certificate = 0;
+static char **tls_allowed_dn_list = NULL;
+
+static char *key_file = (char *) LIBVIRT_SERVERKEY;
+static char *cert_file = (char *) LIBVIRT_SERVERCERT;
+static char *ca_file = (char *) LIBVIRT_CACERT;
+static char *crl_file = (char *) "";
+
+static gnutls_certificate_credentials_t x509_cred;
+static gnutls_dh_params_t dh_params;
+
+static int min_workers = 5;
+static int max_workers = 20;
+static int max_clients = 20;
+
+/* Total number of 'in-process' RPC calls allowed across all clients */
+static int max_requests = 20;
+/* Total number of 'in-process' RPC calls allowed by a single client*/
+static int max_client_requests = 5;
+
+#define DH_BITS 1024
+
+static sig_atomic_t sig_errors = 0;
+static int sig_lasterrno = 0;
+
+static void sig_handler(int sig, siginfo_t * siginfo,
+ void* context ATTRIBUTE_UNUSED) {
+ int origerrno;
+ int r;
+
+ /* set the sig num in the struct */
+ siginfo->si_signo = sig;
+
+ origerrno = errno;
+ r = safewrite(sigwrite, siginfo, sizeof(*siginfo));
+ if (r == -1) {
+ sig_errors++;
+ sig_lasterrno = errno;
+ }
+ errno = origerrno;
+}
+
+static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque);
+static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque);
+static int qemudStartWorker(struct qemud_server *server, struct qemud_worker *worker);
+
+void
+qemudClientMessageQueuePush(struct qemud_client_message **queue,
+ struct qemud_client_message *msg)
+{
+ struct qemud_client_message *tmp = *queue;
+
+ if (tmp) {
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = msg;
+ } else {
+ *queue = msg;
+ }
+}
+
+struct qemud_client_message *
+qemudClientMessageQueueServe(struct qemud_client_message **queue)
+{
+ struct qemud_client_message *tmp = *queue;
+
+ if (tmp) {
+ *queue = tmp->next;
+ tmp->next = NULL;
+ }
+
+ return tmp;
+}
+
+static int
+remoteCheckCertFile(const char *type, const char *file)
+{
+ struct stat sb;
+ if (stat(file, &sb) < 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Cannot access %s '%s': %s"),
+ type, file, virStrerror(errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+remoteInitializeGnuTLS (void)
+{
+ int err;
+
+ /* Initialise GnuTLS. */
+ gnutls_global_init ();
+
+ err = gnutls_certificate_allocate_credentials (&x509_cred);
+ if (err) {
+ VIR_ERROR(_("gnutls_certificate_allocate_credentials: %s"),
+ gnutls_strerror (err));
+ return -1;
+ }
+
+ if (ca_file && ca_file[0] != '\0') {
+ if (remoteCheckCertFile("CA certificate", ca_file) < 0)
+ return -1;
+
+ qemudDebug ("loading CA cert from %s", ca_file);
+ err = gnutls_certificate_set_x509_trust_file (x509_cred, ca_file,
+ GNUTLS_X509_FMT_PEM);
+ if (err < 0) {
+ VIR_ERROR(_("gnutls_certificate_set_x509_trust_file: %s"),
+ gnutls_strerror (err));
+ return -1;
+ }
+ }
+
+ if (crl_file && crl_file[0] != '\0') {
+ if (remoteCheckCertFile("CA revocation list", crl_file) < 0)
+ return -1;
+
+ DEBUG("loading CRL from %s", crl_file);
+ err = gnutls_certificate_set_x509_crl_file (x509_cred, crl_file,
+ GNUTLS_X509_FMT_PEM);
+ if (err < 0) {
+ VIR_ERROR(_("gnutls_certificate_set_x509_crl_file: %s"),
+ gnutls_strerror (err));
+ return -1;
+ }
+ }
+
+ if (cert_file && cert_file[0] != '\0' && key_file && key_file[0] != '\0') {
+ if (remoteCheckCertFile("server certificate", cert_file) < 0)
+ return -1;
+ if (remoteCheckCertFile("server key", key_file) < 0)
+ return -1;
+ DEBUG("loading cert and key from %s and %s", cert_file, key_file);
+ err =
+ gnutls_certificate_set_x509_key_file (x509_cred,
+ cert_file, key_file,
+ GNUTLS_X509_FMT_PEM);
+ if (err < 0) {
+ VIR_ERROR(_("gnutls_certificate_set_x509_key_file: %s"),
+ gnutls_strerror (err));
+ return -1;
+ }
+ }
+
+ /* Generate Diffie Hellman parameters - for use with DHE
+ * kx algorithms. These should be discarded and regenerated
+ * once a day, once a week or once a month. Depending on the
+ * security requirements.
+ */
+ err = gnutls_dh_params_init (&dh_params);
+ if (err < 0) {
+ VIR_ERROR(_("gnutls_dh_params_init: %s"), gnutls_strerror (err));
+ return -1;
+ }
+ err = gnutls_dh_params_generate2 (dh_params, DH_BITS);
+ if (err < 0) {
+ VIR_ERROR(_("gnutls_dh_params_generate2: %s"), gnutls_strerror (err));
+ return -1;
+ }
+
+ gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+ return 0;
+}
+
+static void
+qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED,
+ int fd ATTRIBUTE_UNUSED,
+ int events ATTRIBUTE_UNUSED,
+ void *opaque) {
+ struct qemud_server *server = (struct qemud_server *)opaque;
+ siginfo_t siginfo;
+ int ret;
+
+ virMutexLock(&server->lock);
+
+ if (saferead(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to read from signal pipe: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ virMutexUnlock(&server->lock);
+ return;
+ }
+
+ ret = 0;
+
+ switch (siginfo.si_signo) {
+ case SIGHUP:
+ VIR_INFO0(_("Reloading configuration on SIGHUP"));
+ if (virStateReload() < 0)
+ VIR_WARN0(_("Error while reloading drivers"));
+ break;
+
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ VIR_WARN(_("Shutting down on signal %d"), siginfo.si_signo);
+ server->shutdown = 1;
+ break;
+
+ default:
+ VIR_INFO(_("Received unexpected signal %d"), siginfo.si_signo);
+ break;
+ }
+
+ if (ret != 0)
+ server->shutdown = 1;
+
+ virMutexUnlock(&server->lock);
+}
+
+
+static int qemudGoDaemon(void) {
+ int pid = fork();
+ switch (pid) {
+ case 0:
+ {
+ int stdinfd = -1;
+ int stdoutfd = -1;
+ int nextpid;
+
+ if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
+ goto cleanup;
+ if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0)
+ goto cleanup;
+ if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
+ goto cleanup;
+ if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
+ goto cleanup;
+ if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
+ goto cleanup;
+ if (close(stdinfd) < 0)
+ goto cleanup;
+ stdinfd = -1;
+ if (close(stdoutfd) < 0)
+ goto cleanup;
+ stdoutfd = -1;
+
+ if (setsid() < 0)
+ goto cleanup;
+
+ nextpid = fork();
+ switch (nextpid) {
+ case 0:
+ return 0;
+ case -1:
+ return -1;
+ default:
+ _exit(0);
+ }
+
+ cleanup:
+ if (stdoutfd != -1)
+ close(stdoutfd);
+ if (stdinfd != -1)
+ close(stdinfd);
+ return -1;
+
+ }
+
+ case -1:
+ return -1;
+
+ default:
+ {
+ int got, status = 0;
+ /* We wait to make sure the next child forked
+ successfully */
+ if ((got = waitpid(pid, &status, 0)) < 0 ||
+ got != pid ||
+ status != 0) {
+ return -1;
+ }
+ _exit(0);
+ }
+ }
+}
+
+static int qemudWritePidFile(const char *pidFile) {
+ int fd;
+ FILE *fh;
+ char ebuf[1024];
+
+ if (pidFile[0] == '\0')
+ return 0;
+
+ if ((fd = open(pidFile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
+ VIR_ERROR(_("Failed to open pid file '%s' : %s"),
+ pidFile, virStrerror(errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ if (!(fh = fdopen(fd, "w"))) {
+ VIR_ERROR(_("Failed to fdopen pid file '%s' : %s"),
+ pidFile, virStrerror(errno, ebuf, sizeof ebuf));
+ close(fd);
+ return -1;
+ }
+
+ if (fprintf(fh, "%lu\n", (unsigned long)getpid()) < 0) {
+ VIR_ERROR(_("Failed to write to pid file '%s' : %s"),
+ pidFile, virStrerror(errno, ebuf, sizeof ebuf));
+ fclose(fh);
+ return -1;
+ }
+
+ if (fclose(fh) == EOF) {
+ VIR_ERROR(_("Failed to close pid file '%s' : %s"),
+ pidFile, virStrerror(errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int qemudListenUnix(struct qemud_server *server,
+ const char *path, int readonly, int auth) {
+ struct qemud_socket *sock;
+ struct sockaddr_un addr;
+ mode_t oldmask;
+ gid_t oldgrp;
+ char ebuf[1024];
+
+ if (VIR_ALLOC(sock) < 0) {
+ VIR_ERROR("%s", _("Failed to allocate memory for struct qemud_socket"));
+ return -1;
+ }
+
+ sock->readonly = readonly;
+ sock->port = -1;
+ sock->type = QEMUD_SOCK_TYPE_UNIX;
+ sock->auth = auth;
+
+ if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ VIR_ERROR(_("Failed to create socket: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ goto cleanup;
+ }
+
+ if (virSetCloseExec(sock->fd) < 0 ||
+ virSetNonBlock(sock->fd) < 0)
+ goto cleanup;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
+ if (addr.sun_path[0] == '@')
+ addr.sun_path[0] = '\0';
+
+
+ oldgrp = getgid();
+ oldmask = umask(readonly ? ~unix_sock_ro_mask : ~unix_sock_rw_mask);
+ if (server->privileged)
+ setgid(unix_sock_gid);
+
+ if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ VIR_ERROR(_("Failed to bind socket to '%s': %s"),
+ path, virStrerror(errno, ebuf, sizeof ebuf));
+ goto cleanup;
+ }
+ umask(oldmask);
+ if (server->privileged)
+ setgid(oldgrp);
+
+ if (listen(sock->fd, 30) < 0) {
+ VIR_ERROR(_("Failed to listen for connections on '%s': %s"),
+ path, virStrerror(errno, ebuf, sizeof ebuf));
+ goto cleanup;
+ }
+
+ if ((sock->watch = virEventAddHandleImpl(sock->fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchServerEvent,
+ server, NULL)) < 0) {
+ VIR_ERROR0(_("Failed to add server event callback"));
+ goto cleanup;
+ }
+
+ sock->next = server->sockets;
+ server->sockets = sock;
+ server->nsockets++;
+
+ return 0;
+
+ cleanup:
+ if (sock->fd)
+ close(sock->fd);
+ free(sock);
+ return -1;
+}
+
+// See: http://people.redhat.com/drepper/userapi-ipv6.html
+static int
+remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *node, const char *service)
+{
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ memset (&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ int e = getaddrinfo (node, service, &hints, &ai);
+ if (e != 0) {
+ VIR_ERROR(_("getaddrinfo: %s\n"), gai_strerror (e));
+ return -1;
+ }
+
+ struct addrinfo *runp = ai;
+ while (runp && *nfds_r < max_fds) {
+ char ebuf[1024];
+ fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype,
+ runp->ai_protocol);
+ if (fds[*nfds_r] == -1) {
+ VIR_ERROR(_("socket: %s"), virStrerror (errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ int opt = 1;
+ setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
+
+ if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) {
+ if (errno != EADDRINUSE) {
+ VIR_ERROR(_("bind: %s"), virStrerror (errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+ close (fds[*nfds_r]);
+ }
+ else {
+ if (listen (fds[*nfds_r], SOMAXCONN) == -1) {
+ VIR_ERROR(_("listen: %s"),
+ virStrerror (errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+ ++*nfds_r;
+ }
+ runp = runp->ai_next;
+ }
+
+ freeaddrinfo (ai);
+ return 0;
+}
+
+/* Listen on the named/numbered TCP port. On a machine with IPv4 and
+ * IPv6 interfaces this may generate several sockets.
+ */
+static int
+remoteListenTCP (struct qemud_server *server,
+ const char *addr,
+ const char *port,
+ int type,
+ int auth)
+{
+ int fds[2];
+ int nfds = 0;
+ int i;
+ struct qemud_socket *sock;
+
+ if (remoteMakeSockets (fds, 2, &nfds, addr, port) == -1)
+ return -1;
+
+ for (i = 0; i < nfds; ++i) {
+ union {
+ struct sockaddr_storage sa_stor;
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+#ifdef AF_INET6
+ struct sockaddr_in6 sa_in6;
+#endif
+ } s;
+ char ebuf[1024];
+ socklen_t salen = sizeof(s);
+
+ if (VIR_ALLOC(sock) < 0) {
+ VIR_ERROR(_("remoteListenTCP: calloc: %s"),
+ virStrerror (errno, ebuf, sizeof ebuf));
+ goto cleanup;
+ }
+
+ sock->readonly = 0;
+ sock->next = server->sockets;
+ server->sockets = sock;
+ server->nsockets++;
+
+ sock->fd = fds[i];
+ sock->type = type;
+ sock->auth = auth;
+
+ if (getsockname(sock->fd, &s.sa, &salen) < 0)
+ goto cleanup;
+
+ if (s.sa.sa_family == AF_INET) {
+ sock->port = htons(s.sa_in.sin_port);
+#ifdef AF_INET6
+ } else if (s.sa.sa_family == AF_INET6)
+ sock->port = htons(s.sa_in6.sin6_port);
+#endif
+ else
+ sock->port = -1;
+
+ if (virSetCloseExec(sock->fd) < 0 ||
+ virSetNonBlock(sock->fd) < 0)
+ goto cleanup;
+
+ if (listen (sock->fd, 30) < 0) {
+ VIR_ERROR(_("remoteListenTCP: listen: %s"),
+ virStrerror (errno, ebuf, sizeof ebuf));
+ goto cleanup;
+ }
+
+ if ((sock->watch = virEventAddHandleImpl(sock->fd,
+ VIR_EVENT_HANDLE_READABLE |
+ VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP,
+ qemudDispatchServerEvent,
+ server, NULL)) < 0) {
+ VIR_ERROR0(_("Failed to add server event callback"));
+ goto cleanup;
+ }
+
+ }
+
+ return 0;
+
+cleanup:
+ for (i = 0; i < nfds; ++i)
+ close(fds[i]);
+ return -1;
+}
+
+static int qemudInitPaths(struct qemud_server *server,
+ char *sockname,
+ char *roSockname,
+ int maxlen)
+{
+ char *sock_dir;
+ char *dir_prefix = NULL;
+ int ret = -1;
+ char *sock_dir_prefix = NULL;
+
+ if (unix_sock_dir)
+ sock_dir = unix_sock_dir;
+ else {
+ sock_dir = sockname;
+ if (server->privileged) {
+ dir_prefix = strdup (LOCAL_STATE_DIR);
+ if (dir_prefix == NULL) {
+ virReportOOMError(NULL);
+ goto cleanup;
+ }
+ if (snprintf (sock_dir, maxlen, "%s/run/libvirt",
+ dir_prefix) >= maxlen)
+ goto snprintf_error;
+ } else {
+ uid_t uid = geteuid();
+ dir_prefix = virGetUserDirectory(NULL, uid);
+ if (dir_prefix == NULL) {
+ /* Do not diagnose here; virGetUserDirectory does that. */
+ goto snprintf_error;
+ }
+
+ if (snprintf(sock_dir, maxlen, "%s/.libvirt", dir_prefix) >= maxlen)
+ goto snprintf_error;
+ }
+ }
+
+ sock_dir_prefix = strdup (sock_dir);
+ if (!sock_dir_prefix) {
+ virReportOOMError(NULL);
+ goto cleanup;
+ }
+
+ if (server->privileged) {
+ if (snprintf (sockname, maxlen, "%s/libvirt-sock",
+ sock_dir_prefix) >= maxlen
+ || (snprintf (roSockname, maxlen, "%s/libvirt-sock-ro",
+ sock_dir_prefix) >= maxlen))
+ goto snprintf_error;
+ unlink(sockname);
+ unlink(roSockname);
+ } else {
+ if (snprintf(sockname, maxlen, "@%s/libvirt-sock",
+ sock_dir_prefix) >= maxlen)
+ goto snprintf_error;
+ }
+
+ if (server->privileged)
+ server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt");
+ else
+ virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix);
+
+ if (server->logDir == NULL)
+ virReportOOMError(NULL);
+
+ ret = 0;
+
+ snprintf_error:
+ if (ret)
+ VIR_ERROR("%s",
+ _("Resulting path too long for buffer in qemudInitPaths()"));
+
+ cleanup:
+ free (dir_prefix);
+ free (sock_dir_prefix);
+ return ret;
+}
+
+static struct qemud_server *qemudInitialize(int sigread) {
+ struct qemud_server *server;
+
+ if (VIR_ALLOC(server) < 0) {
+ VIR_ERROR0(_("Failed to allocate struct qemud_server"));
+ return NULL;
+ }
+
+ if (virMutexInit(&server->lock) < 0) {
+ VIR_ERROR("%s", _("cannot initialize mutex"));
+ VIR_FREE(server);
+ }
+ if (virCondInit(&server->job) < 0) {
+ VIR_ERROR("%s", _("cannot initialize condition variable"));
+ virMutexDestroy(&server->lock);
+ VIR_FREE(server);
+ }
+
+ server->privileged = geteuid() == 0 ? 1 : 0;
+ server->sigread = sigread;
+
+ if (virEventInit() < 0) {
+ VIR_ERROR0(_("Failed to initialize event system"));
+ VIR_FREE(server);
+ return NULL;
+ }
+
+ virInitialize();
+
+ /*
+ * Note that the order is important: the first ones have a higher
+ * priority when calling virStateInitialize. We must register
+ * the network, storage and nodedev drivers before any domain
+ * drivers, since their resources must be auto-started before
+ * any domains can be auto-started.
+ */
+#ifdef WITH_DRIVER_MODULES
+ /* We don't care if any of these fail, because the whole point
+ * is to allow users to only install modules they want to use.
+ * If they try to use a open a connection for a module that
+ * is not loaded they'll get a suitable error at that point
+ */
+ virDriverLoadModule("network");
+ virDriverLoadModule("storage");
+ virDriverLoadModule("nodedev");
+ virDriverLoadModule("secret");
+ virDriverLoadModule("qemu");
+ virDriverLoadModule("lxc");
+ virDriverLoadModule("uml");
+ virDriverLoadModule("one");
+#else
+#ifdef WITH_NETWORK
+ networkRegister();
+#endif
+#ifdef WITH_NETCF
+ interfaceRegister();
+#endif
+#ifdef WITH_STORAGE_DIR
+ storageRegister();
+#endif
+#if defined(WITH_NODE_DEVICES) && \
+ (defined(HAVE_HAL) || defined(HAVE_DEVKIT))
+ nodedevRegister();
+#endif
+ secretRegister();
+#ifdef WITH_QEMU
+ qemuRegister();
+#endif
+#ifdef WITH_LXC
+ lxcRegister();
+#endif
+#ifdef WITH_UML
+ umlRegister();
+#endif
+#ifdef WITH_ONE
+ oneRegister();
+#endif
+#endif
+
+ virEventRegisterImpl(virEventAddHandleImpl,
+ virEventUpdateHandleImpl,
+ virEventRemoveHandleImpl,
+ virEventAddTimeoutImpl,
+ virEventUpdateTimeoutImpl,
+ virEventRemoveTimeoutImpl);
+
+ virStateInitialize(server->privileged);
+
+ return server;
+}
+
+static struct qemud_server *qemudNetworkInit(struct qemud_server *server) {
+ struct qemud_socket *sock;
+ char sockname[PATH_MAX];
+ char roSockname[PATH_MAX];
+#if HAVE_SASL
+ int err;
+#endif /* HAVE_SASL */
+
+ roSockname[0] = '\0';
+
+ if (qemudInitPaths(server, sockname, roSockname, PATH_MAX) < 0)
+ goto cleanup;
+
+ if (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0)
+ goto cleanup;
+
+ if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0)
+ goto cleanup;
+
+#if HAVE_SASL
+ if (auth_unix_rw == REMOTE_AUTH_SASL ||
+ auth_unix_ro == REMOTE_AUTH_SASL ||
+ auth_tcp == REMOTE_AUTH_SASL ||
+ auth_tls == REMOTE_AUTH_SASL) {
+ if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) {
+ VIR_ERROR(_("Failed to initialize SASL authentication %s"),
+ sasl_errstring(err, NULL, NULL));
+ goto cleanup;
+ }
+ }
+#endif
+
+#if HAVE_POLKIT0
+ if (auth_unix_rw == REMOTE_AUTH_POLKIT ||
+ auth_unix_ro == REMOTE_AUTH_POLKIT) {
+ DBusError derr;
+
+ dbus_connection_set_change_sigpipe(FALSE);
+ dbus_threads_init_default();
+
+ dbus_error_init(&derr);
+ server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr);
+ if (!(server->sysbus)) {
+ VIR_ERROR(_("Failed to connect to system bus for PolicyKit auth: %s"),
+ derr.message);
+ dbus_error_free(&derr);
+ goto cleanup;
+ }
+ dbus_connection_set_exit_on_disconnect(server->sysbus, FALSE);
+ }
+#endif
+
+ if (ipsock) {
+ if (listen_tcp && remoteListenTCP (server, listen_addr, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0)
+ goto cleanup;
+
+ if (listen_tls) {
+ if (remoteInitializeGnuTLS () < 0)
+ goto cleanup;
+
+ if (remoteListenTCP (server, listen_addr, tls_port, QEMUD_SOCK_TYPE_TLS, auth_tls) < 0)
+ goto cleanup;
+ }
+ }
+
+#ifdef HAVE_AVAHI
+ if (server->privileged && mdns_adv) {
+ struct libvirtd_mdns_group *group;
+ int port = 0;
+
+ server->mdns = libvirtd_mdns_new();
+
+ if (!mdns_name) {
+ char groupname[64], localhost[HOST_NAME_MAX+1], *tmp;
+ /* Extract the host part of the potentially FQDN */
+ gethostname(localhost, HOST_NAME_MAX);
+ localhost[HOST_NAME_MAX] = '\0';
+ if ((tmp = strchr(localhost, '.')))
+ *tmp = '\0';
+ snprintf(groupname, sizeof(groupname)-1, "Virtualization Host %s", localhost);
+ groupname[sizeof(groupname)-1] = '\0';
+ group = libvirtd_mdns_add_group(server->mdns, groupname);
+ } else {
+ group = libvirtd_mdns_add_group(server->mdns, mdns_name);
+ }
+
+ /*
+ * See if there's a TLS enabled port we can advertise. Cowardly
+ * don't bother to advertise TCP since we don't want people using
+ * them for real world apps
+ */
+ sock = server->sockets;
+ while (sock) {
+ if (sock->port != -1 && sock->type == QEMUD_SOCK_TYPE_TLS) {
+ port = sock->port;
+ break;
+ }
+ sock = sock->next;
+ }
+
+ /*
+ * Add the primary entry - we choose SSH because its most likely to always
+ * be available
+ */
+ libvirtd_mdns_add_entry(group, "_libvirt._tcp", port);
+ libvirtd_mdns_start(server->mdns);
+ }
+#endif
+
+ return server;
+
+ cleanup:
+ if (server) {
+ sock = server->sockets;
+ while (sock) {
+ close(sock->fd);
+ sock = sock->next;
+ }
+
+#if HAVE_POLKIT0
+ if (server->sysbus)
+ dbus_connection_unref(server->sysbus);
+#endif
+ free(server);
+ }
+ return NULL;
+}
+
+static gnutls_session_t
+remoteInitializeTLSSession (void)
+{
+ gnutls_session_t session;
+ int err;
+
+ err = gnutls_init (&session, GNUTLS_SERVER);
+ if (err != 0) goto failed;
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ err = gnutls_set_default_priority (session);
+ if (err != 0) goto failed;
+
+ err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ if (err != 0) goto failed;
+
+ /* request client certificate if any.
+ */
+ gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
+
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ return session;
+
+ failed:
+ VIR_ERROR(_("remoteInitializeTLSSession: %s"),
+ gnutls_strerror (err));
+ return NULL;
+}
+
+/* Check DN is on tls_allowed_dn_list. */
+static int
+remoteCheckDN (gnutls_x509_crt_t cert)
+{
+ char name[256];
+ size_t namesize = sizeof name;
+ char **wildcards;
+ int err;
+
+ err = gnutls_x509_crt_get_dn (cert, name, &namesize);
+ if (err != 0) {
+ VIR_ERROR(_("remoteCheckDN: gnutls_x509_cert_get_dn: %s"),
+ gnutls_strerror (err));
+ return 0;
+ }
+
+ /* If the list is not set, allow any DN. */
+ wildcards = tls_allowed_dn_list;
+ if (!wildcards)
+ return 1;
+
+ while (*wildcards) {
+ if (fnmatch (*wildcards, name, 0) == 0)
+ return 1;
+ wildcards++;
+ }
+
+ /* Print the client's DN. */
+ DEBUG(_("remoteCheckDN: failed: client DN is %s"), name);
+
+ return 0; // Not found.
+}
+
+static int
+remoteCheckCertificate (gnutls_session_t session)
+{
+ int ret;
+ unsigned int status;
+ const gnutls_datum_t *certs;
+ unsigned int nCerts, i;
+ time_t now;
+
+ if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0){
+ VIR_ERROR(_("remoteCheckCertificate: verify failed: %s"),
+ gnutls_strerror (ret));
+ return -1;
+ }
+
+ if (status != 0) {
+ if (status & GNUTLS_CERT_INVALID)
+ VIR_ERROR0(_("remoteCheckCertificate: "
+ "the client certificate is not trusted."));
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ VIR_ERROR0(_("remoteCheckCertificate: the client "
+ "certificate has unknown issuer."));
+
+ if (status & GNUTLS_CERT_REVOKED)
+ VIR_ERROR0(_("remoteCheckCertificate: "
+ "the client certificate has been revoked."));
+
+#ifndef GNUTLS_1_0_COMPAT
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ VIR_ERROR0(_("remoteCheckCertificate: the client certificate"
+ " uses an insecure algorithm."));
+#endif
+
+ return -1;
+ }
+
+ if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+ VIR_ERROR0(_("remoteCheckCertificate: certificate is not X.509"));
+ return -1;
+ }
+
+ if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) {
+ VIR_ERROR0(_("remoteCheckCertificate: no peers"));
+ return -1;
+ }
+
+ now = time (NULL);
+
+ for (i = 0; i < nCerts; i++) {
+ gnutls_x509_crt_t cert;
+
+ if (gnutls_x509_crt_init (&cert) < 0) {
+ VIR_ERROR0(_("remoteCheckCertificate: gnutls_x509_crt_init failed"));
+ return -1;
+ }
+
+ if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+ VIR_ERROR0(_("remoteCheckCertificate: "
+ "the client certificate has expired"));
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_activation_time (cert) > now) {
+ VIR_ERROR0(_("remoteCheckCertificate: the client "
+ "certificate is not yet activated"));
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (i == 0) {
+ if (!remoteCheckDN (cert)) {
+ /* This is the most common error: make it informative. */
+ VIR_ERROR0(_("remoteCheckCertificate: client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list). Use 'openssl x509 -in clientcert.pem -text' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option."));
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Check the client's access. */
+static int
+remoteCheckAccess (struct qemud_client *client)
+{
+ struct qemud_client_message *confirm;
+
+ /* Verify client certificate. */
+ if (remoteCheckCertificate (client->tlssession) == -1) {
+ VIR_ERROR0(_("remoteCheckCertificate: "
+ "failed to verify client's certificate"));
+ if (!tls_no_verify_certificate) return -1;
+ else VIR_INFO0(_("remoteCheckCertificate: tls_no_verify_certificate "
+ "is set so the bad certificate is ignored"));
+ }
+
+ if (client->tx) {
+ VIR_INFO("%s",
+ _("client had unexpected data pending tx after access check"));
+ return -1;
+ }
+
+ if (VIR_ALLOC(confirm) < 0)
+ return -1;
+
+ /* Checks have succeeded. Write a '\1' byte back to the client to
+ * indicate this (otherwise the socket is abruptly closed).
+ * (NB. The '\1' byte is sent in an encrypted record).
+ */
+ confirm->async = 1;
+ confirm->bufferLength = 1;
+ confirm->bufferOffset = 0;
+ confirm->buffer[0] = '\1';
+
+ client->tx = confirm;
+ return 0;
+}
+
+#if HAVE_POLKIT
+int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid) {
+#ifdef SO_PEERCRED
+ struct ucred cr;
+ unsigned int cr_len = sizeof (cr);
+
+ if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to verify client credentials: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ *pid = cr.pid;
+ *uid = cr.uid;
+#else
+ /* XXX Many more OS support UNIX socket credentials we could port to. See dbus ....*/
+#error "UNIX socket credentials not supported/implemented on this platform yet..."
+#endif
+ return 0;
+}
+#endif
+
+
+static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) {
+ int fd;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = (socklen_t) (sizeof addr);
+ struct qemud_client *client;
+ int no_slow_start = 1;
+
+ if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) {
+ char ebuf[1024];
+ if (errno == EAGAIN)
+ return 0;
+ VIR_ERROR(_("Failed to accept connection: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ if (server->nclients >= max_clients) {
+ VIR_ERROR(_("Too many active clients (%d), dropping connection"), max_clients);
+ close(fd);
+ return -1;
+ }
+
+ if (VIR_REALLOC_N(server->clients, server->nclients+1) < 0) {
+ VIR_ERROR0(_("Out of memory allocating clients"));
+ close(fd);
+ return -1;
+ }
+
+#ifdef __sun
+ {
+ ucred_t *ucred = NULL;
+ const priv_set_t *privs;
+
+ if (getpeerucred (fd, &ucred) == -1 ||
+ (privs = ucred_getprivset (ucred, PRIV_EFFECTIVE)) == NULL) {
+ if (ucred != NULL)
+ ucred_free (ucred);
+ close (fd);
+ return -1;
+ }
+
+ if (!priv_ismember (privs, PRIV_VIRT_MANAGE)) {
+ ucred_free (ucred);
+ close (fd);
+ return -1;
+ }
+
+ ucred_free (ucred);
+ }
+#endif /* __sun */
+
+ /* Disable Nagle. Unix sockets will ignore this. */
+ setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start,
+ sizeof no_slow_start);
+
+ if (virSetCloseExec(fd) < 0 ||
+ virSetNonBlock(fd) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ if (VIR_ALLOC(client) < 0)
+ goto cleanup;
+ if (virMutexInit(&client->lock) < 0) {
+ VIR_ERROR("%s", _("cannot initialize mutex"));
+ VIR_FREE(client);
+ goto cleanup;
+ }
+
+ client->magic = QEMUD_CLIENT_MAGIC;
+ client->fd = fd;
+ client->readonly = sock->readonly;
+ client->type = sock->type;
+ client->auth = sock->auth;
+ memcpy (&client->addr, &addr, sizeof addr);
+ client->addrlen = addrlen;
+
+ /* Prepare one for packet receive */
+ if (VIR_ALLOC(client->rx) < 0)
+ goto cleanup;
+ client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
+
+
+#if HAVE_POLKIT
+ /* Only do policy checks for non-root - allow root user
+ through with no checks, as a fail-safe - root can easily
+ change policykit policy anyway, so its pointless trying
+ to restrict root */
+ if (client->auth == REMOTE_AUTH_POLKIT) {
+ uid_t uid;
+ pid_t pid;
+
+ if (qemudGetSocketIdentity(client->fd, &uid, &pid) < 0)
+ goto cleanup;
+
+ /* Client is running as root, so disable auth */
+ if (uid == 0) {
+ VIR_INFO(_("Turn off polkit auth for privileged client %d"), pid);
+ client->auth = REMOTE_AUTH_NONE;
+ }
+ }
+#endif
+
+ if (client->type != QEMUD_SOCK_TYPE_TLS) {
+ /* Plain socket, so prepare to read first message */
+ if (qemudRegisterClientEvent (server, client) < 0)
+ goto cleanup;
+ } else {
+ int ret;
+
+ client->tlssession = remoteInitializeTLSSession ();
+ if (client->tlssession == NULL)
+ goto cleanup;
+
+ gnutls_transport_set_ptr (client->tlssession,
+ (gnutls_transport_ptr_t) (long) fd);
+
+ /* Begin the TLS handshake. */
+ ret = gnutls_handshake (client->tlssession);
+ if (ret == 0) {
+ client->handshake = 0;
+
+ /* Unlikely, but ... Next step is to check the certificate. */
+ if (remoteCheckAccess (client) == -1)
+ goto cleanup;
+
+ /* Handshake & cert check OK, so prepare to read first message */
+ if (qemudRegisterClientEvent(server, client) < 0)
+ goto cleanup;
+ } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ /* Most likely, need to do more handshake data */
+ client->handshake = 1;
+
+ if (qemudRegisterClientEvent (server, client) < 0)
+ goto cleanup;
+ } else {
+ VIR_ERROR(_("TLS handshake failed: %s"),
+ gnutls_strerror (ret));
+ goto cleanup;
+ }
+ }
+
+ server->clients[server->nclients++] = client;
+
+ if (server->nclients > server->nactiveworkers &&
+ server->nactiveworkers < server->nworkers) {
+ int i;
+ for (i = 0 ; i < server->nworkers ; i++) {
+ if (!server->workers[i].hasThread) {
+ if (qemudStartWorker(server, &server->workers[i]) < 0)
+ return -1;
+ server->nactiveworkers++;
+ break;
+ }
+ }
+ }
+
+
+ return 0;
+
+ cleanup:
+ if (client &&
+ client->tlssession) gnutls_deinit (client->tlssession);
+ close (fd);
+ VIR_FREE(client->rx);
+ VIR_FREE(client);
+ return -1;
+}
+
+
+/*
+ * You must hold lock for at least the client
+ * We don't free stuff here, merely disconnect the client's
+ * network socket & resources.
+ * We keep the libvirt connection open until any async
+ * jobs have finished, then clean it up elsehwere
+ */
+void qemudDispatchClientFailure(struct qemud_client *client) {
+ if (client->watch != -1) {
+ virEventRemoveHandleImpl(client->watch);
+ client->watch = -1;
+ }
+
+ /* Deregister event delivery callback */
+ if(client->conn) {
+ DEBUG0("Deregistering to relay remote events");
+ virConnectDomainEventDeregister(client->conn, remoteRelayDomainEvent);
+ }
+
+#if HAVE_SASL
+ if (client->saslconn) {
+ sasl_dispose(&client->saslconn);
+ client->saslconn = NULL;
+ }
+ free(client->saslUsername);
+ client->saslUsername = NULL;
+#endif
+ if (client->tlssession) {
+ gnutls_deinit (client->tlssession);
+ client->tlssession = NULL;
+ }
+ if (client->fd != -1) {
+ close(client->fd);
+ client->fd = -1;
+ }
+}
+
+
+/* Caller must hold server lock */
+static struct qemud_client *qemudPendingJob(struct qemud_server *server)
+{
+ int i;
+ for (i = 0 ; i < server->nclients ; i++) {
+ virMutexLock(&server->clients[i]->lock);
+ if (server->clients[i]->dx) {
+ /* Delibrately don't unlock client - caller wants the lock */
+ return server->clients[i];
+ }
+ virMutexUnlock(&server->clients[i]->lock);
+ }
+ return NULL;
+}
+
+static void *qemudWorker(void *data)
+{
+ struct qemud_worker *worker = data;
+ struct qemud_server *server = worker->server;
+
+ while (1) {
+ struct qemud_client *client = NULL;
+ struct qemud_client_message *msg;
+
+ virMutexLock(&server->lock);
+ while (((client = qemudPendingJob(server)) == NULL) &&
+ !worker->quitRequest) {
+ if (virCondWait(&server->job, &server->lock) < 0) {
+ virMutexUnlock(&server->lock);
+ return NULL;
+ }
+ }
+ if (worker->quitRequest) {
+ if (client)
+ virMutexUnlock(&client->lock);
+ virMutexUnlock(&server->lock);
+ return NULL;
+ }
+ worker->processingCall = 1;
+ virMutexUnlock(&server->lock);
+
+ /* We own a locked client now... */
+ client->refs++;
+
+ /* Remove our message from dispatch queue while we use it */
+ msg = qemudClientMessageQueueServe(&client->dx);
+
+ /* This function drops the lock during dispatch,
+ * and re-acquires it before returning */
+ if (remoteDispatchClientRequest (server, client, msg) < 0) {
+ VIR_FREE(msg);
+ qemudDispatchClientFailure(client);
+ client->refs--;
+ virMutexUnlock(&client->lock);
+ continue;
+ }
+
+ client->refs--;
+ virMutexUnlock(&client->lock);
+
+ virMutexLock(&server->lock);
+ worker->processingCall = 0;
+ virMutexUnlock(&server->lock);
+ }
+}
+
+static int qemudStartWorker(struct qemud_server *server,
+ struct qemud_worker *worker) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ /* We want to join workers, so don't detach them */
+ /*pthread_attr_setdetachstate(&attr, 1);*/
+
+ if (worker->hasThread)
+ return -1;
+
+ worker->server = server;
+ worker->hasThread = 1;
+ worker->quitRequest = 0;
+ worker->processingCall = 0;
+
+ if (pthread_create(&worker->thread,
+ &attr,
+ qemudWorker,
+ worker) != 0) {
+ worker->hasThread = 0;
+ worker->server = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Read data into buffer using wire decoding (plain or TLS)
+ *
+ * Returns:
+ * -1 on error or EOF
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static ssize_t qemudClientReadBuf(struct qemud_client *client,
+ char *data, ssize_t len) {
+ ssize_t ret;
+
+ if (len < 0) {
+ VIR_ERROR(_("unexpected negative length request %lld"),
+ (long long int) len);
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+
+ /*qemudDebug ("qemudClientRead: len = %d", len);*/
+
+ if (!client->tlssession) {
+ char ebuf[1024];
+ ret = read (client->fd, data, len);
+ if (ret == -1 && (errno == EAGAIN ||
+ errno == EINTR))
+ return 0;
+ if (ret <= 0) {
+ if (ret != 0)
+ VIR_ERROR(_("read: %s"),
+ virStrerror (errno, ebuf, sizeof ebuf));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+ } else {
+ ret = gnutls_record_recv (client->tlssession, data, len);
+
+ if (ret < 0 && (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED))
+ return 0;
+ if (ret <= 0) {
+ if (ret != 0)
+ VIR_ERROR(_("gnutls_record_recv: %s"),
+ gnutls_strerror (ret));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Read data into buffer without decoding
+ *
+ * Returns:
+ * -1 on error or EOF
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static ssize_t qemudClientReadPlain(struct qemud_client *client) {
+ ssize_t ret;
+ ret = qemudClientReadBuf(client,
+ client->rx->buffer + client->rx->bufferOffset,
+ client->rx->bufferLength - client->rx->bufferOffset);
+ if (ret <= 0)
+ return ret; /* -1 error, 0 eagain */
+
+ client->rx->bufferOffset += ret;
+ return ret;
+}
+
+#if HAVE_SASL
+/*
+ * Read data into buffer decoding with SASL
+ *
+ * Returns:
+ * -1 on error or EOF
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static ssize_t qemudClientReadSASL(struct qemud_client *client) {
+ ssize_t got, want;
+
+ /* We're doing a SSF data read, so now its times to ensure
+ * future writes are under SSF too.
+ *
+ * cf remoteSASLCheckSSF in remote.c
+ */
+ client->saslSSF |= QEMUD_SASL_SSF_WRITE;
+
+ /* Need to read some more data off the wire */
+ if (client->saslDecoded == NULL) {
+ int ret;
+ char encoded[8192];
+ ssize_t encodedLen = sizeof(encoded);
+ encodedLen = qemudClientReadBuf(client, encoded, encodedLen);
+
+ if (encodedLen <= 0)
+ return encodedLen;
+
+ ret = sasl_decode(client->saslconn, encoded, encodedLen,
+ &client->saslDecoded, &client->saslDecodedLength);
+ if (ret != SASL_OK) {
+ VIR_ERROR(_("failed to decode SASL data %s"),
+ sasl_errstring(ret, NULL, NULL));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+
+ client->saslDecodedOffset = 0;
+ }
+
+ /* Some buffered decoded data to return now */
+ got = client->saslDecodedLength - client->saslDecodedOffset;
+ want = client->rx->bufferLength - client->rx->bufferOffset;
+
+ if (want > got)
+ want = got;
+
+ memcpy(client->rx->buffer + client->rx->bufferOffset,
+ client->saslDecoded + client->saslDecodedOffset, want);
+ client->saslDecodedOffset += want;
+ client->rx->bufferOffset += want;
+
+ if (client->saslDecodedOffset == client->saslDecodedLength) {
+ client->saslDecoded = NULL;
+ client->saslDecodedOffset = client->saslDecodedLength = 0;
+ }
+
+ return want;
+}
+#endif
+
+/*
+ * Read as much data off wire as possible till we fill our
+ * buffer, or would block on I/O
+ */
+static ssize_t qemudClientRead(struct qemud_client *client) {
+#if HAVE_SASL
+ if (client->saslSSF & QEMUD_SASL_SSF_READ)
+ return qemudClientReadSASL(client);
+ else
+#endif
+ return qemudClientReadPlain(client);
+}
+
+
+/*
+ * Read data until we get a complete message to process
+ */
+static void qemudDispatchClientRead(struct qemud_server *server,
+ struct qemud_client *client) {
+ /*qemudDebug ("qemudDispatchClientRead: mode = %d", client->mode);*/
+
+readmore:
+ if (qemudClientRead(client) < 0)
+ return; /* Error */
+
+ if (client->rx->bufferOffset < client->rx->bufferLength)
+ return; /* Still not read enough */
+
+ /* Either done with length word header */
+ if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) {
+ unsigned int len;
+ XDR x;
+
+ xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE);
+
+ if (!xdr_u_int(&x, &len)) {
+ xdr_destroy (&x);
+ DEBUG0("Failed to decode packet length");
+ qemudDispatchClientFailure(client);
+ return;
+ }
+ xdr_destroy (&x);
+
+ if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) {
+ DEBUG("Packet length %u too small", len);
+ qemudDispatchClientFailure(client);
+ return;
+ }
+
+ /* Length includes the size of the length word itself */
+ len -= REMOTE_MESSAGE_HEADER_XDR_LEN;
+
+ if (len > REMOTE_MESSAGE_MAX) {
+ DEBUG("Packet length %u too large", len);
+ qemudDispatchClientFailure(client);
+ return;
+ }
+
+ /* Prepare to read rest of message */
+ client->rx->bufferLength += len;
+
+ qemudUpdateClientEvent(client);
+
+ /* Try and read payload immediately instead of going back
+ into poll() because chances are the data is already
+ waiting for us */
+ goto readmore;
+ } else {
+ /* Grab the completed message */
+ struct qemud_client_message *msg = qemudClientMessageQueueServe(&client->rx);
+ struct qemud_client_filter *filter;
+
+ /* Decode the header so we can use it for routing decisions */
+ if (remoteDecodeClientMessageHeader(msg) < 0) {
+ VIR_FREE(msg);
+ qemudDispatchClientFailure(client);
+ }
+
+ /* Check if any filters match this message */
+ filter = client->filters;
+ while (filter) {
+ if ((filter->query)(msg, filter->opaque)) {
+ qemudClientMessageQueuePush(&filter->dx, msg);
+ msg = NULL;
+ break;
+ }
+ filter = filter->next;
+ }
+
+ /* Move completed message to the end of the dispatch queue */
+ if (msg)
+ qemudClientMessageQueuePush(&client->dx, msg);
+ client->nrequests++;
+
+ /* Possibly need to create another receive buffer */
+ if ((client->nrequests < max_client_requests &&
+ VIR_ALLOC(client->rx) < 0)) {
+ qemudDispatchClientFailure(client);
+ } else {
+ if (client->rx)
+ client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
+
+ qemudUpdateClientEvent(client);
+
+ /* Tell one of the workers to get on with it... */
+ virCondSignal(&server->job);
+ }
+ }
+}
+
+
+/*
+ * Send a chunk of data using wire encoding (plain or TLS)
+ *
+ * Returns:
+ * -1 on error
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static ssize_t qemudClientWriteBuf(struct qemud_client *client,
+ const char *data, ssize_t len) {
+ ssize_t ret;
+
+ if (len < 0) {
+ VIR_ERROR(_("unexpected negative length request %lld"),
+ (long long int) len);
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+
+ if (!client->tlssession) {
+ char ebuf[1024];
+ if ((ret = write(client->fd, data, len)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ VIR_ERROR(_("write: %s"), virStrerror (errno, ebuf, sizeof ebuf));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+ } else {
+ ret = gnutls_record_send (client->tlssession, data, len);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INTERRUPTED ||
+ ret == GNUTLS_E_AGAIN)
+ return 0;
+
+ VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Send client->tx using no encoding
+ *
+ * Returns:
+ * -1 on error or EOF
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static int qemudClientWritePlain(struct qemud_client *client) {
+ int ret = qemudClientWriteBuf(client,
+ client->tx->buffer + client->tx->bufferOffset,
+ client->tx->bufferLength - client->tx->bufferOffset);
+ if (ret <= 0)
+ return ret; /* -1 error, 0 = egain */
+ client->tx->bufferOffset += ret;
+ return ret;
+}
+
+
+#if HAVE_SASL
+/*
+ * Send client->tx using SASL encoding
+ *
+ * Returns:
+ * -1 on error
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static int qemudClientWriteSASL(struct qemud_client *client) {
+ int ret;
+
+ /* Not got any pending encoded data, so we need to encode raw stuff */
+ if (client->saslEncoded == NULL) {
+ ret = sasl_encode(client->saslconn,
+ client->tx->buffer + client->tx->bufferOffset,
+ client->tx->bufferLength - client->tx->bufferOffset,
+ &client->saslEncoded,
+ &client->saslEncodedLength);
+
+ if (ret != SASL_OK) {
+ VIR_ERROR(_("failed to encode SASL data %s"),
+ sasl_errstring(ret, NULL, NULL));
+ qemudDispatchClientFailure(client);
+ return -1;
+ }
+
+ client->saslEncodedOffset = 0;
+ }
+
+ /* Send some of the encoded stuff out on the wire */
+ ret = qemudClientWriteBuf(client,
+ client->saslEncoded + client->saslEncodedOffset,
+ client->saslEncodedLength - client->saslEncodedOffset);
+
+ if (ret <= 0)
+ return ret; /* -1 error, 0 == egain */
+
+ /* Note how much we sent */
+ client->saslEncodedOffset += ret;
+
+ /* Sent all encoded, so update raw buffer to indicate completion */
+ if (client->saslEncodedOffset == client->saslEncodedLength) {
+ client->saslEncoded = NULL;
+ client->saslEncodedOffset = client->saslEncodedLength = 0;
+
+ /* Mark as complete, so caller detects completion */
+ client->tx->bufferOffset = client->tx->bufferLength;
+ }
+
+ return ret;
+}
+#endif
+
+/*
+ * Send as much data in the client->tx as possible
+ *
+ * Returns:
+ * -1 on error or EOF
+ * 0 on EAGAIN
+ * n number of bytes
+ */
+static ssize_t qemudClientWrite(struct qemud_client *client) {
+#if HAVE_SASL
+ if (client->saslSSF & QEMUD_SASL_SSF_WRITE)
+ return qemudClientWriteSASL(client);
+ else
+#endif
+ return qemudClientWritePlain(client);
+}
+
+
+/*
+ * Process all queued client->tx messages until
+ * we would block on I/O
+ */
+static void
+qemudDispatchClientWrite(struct qemud_client *client) {
+ while (client->tx) {
+ ssize_t ret;
+
+ ret = qemudClientWrite(client);
+ if (ret < 0) {
+ qemudDispatchClientFailure(client);
+ return;
+ }
+ if (ret == 0)
+ return; /* Would block on write EAGAIN */
+
+ if (client->tx->bufferOffset == client->tx->bufferLength) {
+ struct qemud_client_message *reply;
+
+ /* Get finished reply from head of tx queue */
+ reply = qemudClientMessageQueueServe(&client->tx);
+
+ /* If its not an async message, then we have
+ * just completed an RPC request */
+ if (!reply->async)
+ client->nrequests--;
+
+ /* Move record to end of 'rx' ist */
+ if (!client->rx &&
+ client->nrequests < max_client_requests) {
+ /* Reset message record for next RX attempt */
+ client->rx = reply;
+ client->rx->bufferOffset = 0;
+ client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
+ } else {
+ VIR_FREE(reply);
+ }
+
+ if (client->closing)
+ qemudDispatchClientFailure(client);
+ else
+ qemudUpdateClientEvent(client);
+ }
+ }
+}
+
+static void
+qemudDispatchClientHandshake(struct qemud_client *client) {
+ int ret;
+ /* Continue the handshake. */
+ ret = gnutls_handshake (client->tlssession);
+ if (ret == 0) {
+ client->handshake = 0;
+
+ /* Finished. Next step is to check the certificate. */
+ if (remoteCheckAccess (client) == -1)
+ qemudDispatchClientFailure(client);
+ else
+ qemudUpdateClientEvent(client);
+ } else if (ret == GNUTLS_E_AGAIN ||
+ ret == GNUTLS_E_INTERRUPTED) {
+ /* Carry on waiting for more handshake. Update
+ the events just in case handshake data flow
+ direction has changed */
+ qemudUpdateClientEvent (client);
+ } else {
+ /* Fatal error in handshake */
+ VIR_ERROR(_("TLS handshake failed: %s"),
+ gnutls_strerror (ret));
+ qemudDispatchClientFailure(client);
+ }
+}
+
+static void
+qemudDispatchClientEvent(int watch, int fd, int events, void *opaque) {
+ struct qemud_server *server = (struct qemud_server *)opaque;
+ struct qemud_client *client = NULL;
+ int i;
+
+ virMutexLock(&server->lock);
+
+ for (i = 0 ; i < server->nclients ; i++) {
+ virMutexLock(&server->clients[i]->lock);
+ if (server->clients[i]->watch == watch) {
+ client = server->clients[i];
+ break;
+ }
+ virMutexUnlock(&server->clients[i]->lock);
+ }
+
+ virMutexUnlock(&server->lock);
+
+ if (!client) {
+ return;
+ }
+
+ if (client->fd != fd) {
+ virMutexUnlock(&client->lock);
+ return;
+ }
+
+ if (events & (VIR_EVENT_HANDLE_WRITABLE |
+ VIR_EVENT_HANDLE_READABLE)) {
+ if (client->handshake) {
+ qemudDispatchClientHandshake(client);
+ } else {
+ if (events & VIR_EVENT_HANDLE_WRITABLE)
+ qemudDispatchClientWrite(client);
+ if (events & VIR_EVENT_HANDLE_READABLE)
+ qemudDispatchClientRead(server, client);
+ }
+ }
+
+ /* NB, will get HANGUP + READABLE at same time upon
+ * disconnect */
+ if (events & (VIR_EVENT_HANDLE_ERROR |
+ VIR_EVENT_HANDLE_HANGUP))
+ qemudDispatchClientFailure(client);
+
+ virMutexUnlock(&client->lock);
+}
+
+
+/*
+ * @client: a locked client object
+ */
+static int
+qemudCalculateHandleMode(struct qemud_client *client) {
+ int mode = 0;
+
+ if (client->handshake) {
+ if (gnutls_record_get_direction (client->tlssession) == 0)
+ mode |= VIR_EVENT_HANDLE_READABLE;
+ else
+ mode |= VIR_EVENT_HANDLE_WRITABLE;
+ } else {
+ /* If there is a message on the rx queue then
+ * we're wanting more input */
+ if (client->rx)
+ mode |= VIR_EVENT_HANDLE_READABLE;
+
+ /* If there are one or more messages to send back to client,
+ then monitor for writability on socket */
+ if (client->tx)
+ mode |= VIR_EVENT_HANDLE_WRITABLE;
+ }
+
+ return mode;
+}
+
+/*
+ * @server: a locked or unlocked server object
+ * @client: a locked client object
+ */
+int qemudRegisterClientEvent(struct qemud_server *server,
+ struct qemud_client *client) {
+ int mode;
+
+ mode = qemudCalculateHandleMode(client);
+
+ if ((client->watch = virEventAddHandleImpl(client->fd,
+ mode,
+ qemudDispatchClientEvent,
+ server, NULL)) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * @client: a locked client object
+ */
+void qemudUpdateClientEvent(struct qemud_client *client) {
+ int mode;
+
+ mode = qemudCalculateHandleMode(client);
+
+ virEventUpdateHandleImpl(client->watch, mode);
+}
+
+
+static void
+qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) {
+ struct qemud_server *server = (struct qemud_server *)opaque;
+ struct qemud_socket *sock;
+
+ virMutexLock(&server->lock);
+
+ sock = server->sockets;
+
+ while (sock) {
+ if (sock->watch == watch)
+ break;
+
+ sock = sock->next;
+ }
+
+ if (sock && sock->fd == fd && events)
+ qemudDispatchServer(server, sock);
+
+ virMutexUnlock(&server->lock);
+}
+
+
+static int qemudOneLoop(void) {
+ sig_atomic_t errors;
+
+ if (virEventRunOnce() < 0)
+ return -1;
+
+ /* Check for any signal handling errors and log them. */
+ errors = sig_errors;
+ if (errors) {
+ char ebuf[1024];
+ sig_errors -= errors;
+ VIR_ERROR(_("Signal handler reported %d errors: last error: %s"),
+ errors, virStrerror (sig_lasterrno, ebuf, sizeof ebuf));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void qemudInactiveTimer(int timerid, void *data) {
+ struct qemud_server *server = (struct qemud_server *)data;
+
+ if (virStateActive() ||
+ server->clients) {
+ DEBUG0("Timer expired but still active, not shutting down");
+ virEventUpdateTimeoutImpl(timerid, -1);
+ } else {
+ DEBUG0("Timer expired and inactive, shutting down");
+ server->shutdown = 1;
+ }
+}
+
+static void qemudFreeClient(struct qemud_client *client) {
+ while (client->rx) {
+ struct qemud_client_message *msg
+ = qemudClientMessageQueueServe(&client->rx);
+ VIR_FREE(msg);
+ }
+ while (client->dx) {
+ struct qemud_client_message *msg
+ = qemudClientMessageQueueServe(&client->dx);
+ VIR_FREE(msg);
+ }
+ while (client->tx) {
+ struct qemud_client_message *msg
+ = qemudClientMessageQueueServe(&client->tx);
+ VIR_FREE(msg);
+ }
+
+ if (client->conn)
+ virConnectClose(client->conn);
+ virMutexDestroy(&client->lock);
+ VIR_FREE(client);
+}
+
+static int qemudRunLoop(struct qemud_server *server) {
+ int timerid = -1;
+ int ret = -1, i;
+ int timerActive = 0;
+
+ virMutexLock(&server->lock);
+
+ if (timeout > 0 &&
+ (timerid = virEventAddTimeoutImpl(-1,
+ qemudInactiveTimer,
+ server, NULL)) < 0) {
+ VIR_ERROR0(_("Failed to register shutdown timeout"));
+ return -1;
+ }
+
+ if (min_workers > max_workers)
+ max_workers = min_workers;
+
+ server->nworkers = max_workers;
+ if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) {
+ VIR_ERROR0(_("Failed to allocate workers"));
+ return -1;
+ }
+
+ for (i = 0 ; i < min_workers ; i++) {
+ if (qemudStartWorker(server, &server->workers[i]) < 0)
+ goto cleanup;
+ server->nactiveworkers++;
+ }
+
+ for (;;) {
+ /* A shutdown timeout is specified, so check
+ * if any drivers have active state, if not
+ * shutdown after timeout seconds
+ */
+ if (timeout > 0) {
+ if (timerActive) {
+ if (server->clients) {
+ DEBUG("Deactivating shutdown timer %d", timerid);
+ virEventUpdateTimeoutImpl(timerid, -1);
+ timerActive = 0;
+ }
+ } else {
+ if (!virStateActive() &&
+ !server->clients) {
+ DEBUG("Activating shutdown timer %d", timerid);
+ virEventUpdateTimeoutImpl(timerid, timeout * 1000);
+ timerActive = 1;
+ }
+ }
+ }
+
+ virMutexUnlock(&server->lock);
+ if (qemudOneLoop() < 0) {
+ virMutexLock(&server->lock);
+ DEBUG0("Loop iteration error, exiting\n");
+ break;
+ }
+ virMutexLock(&server->lock);
+
+ reprocess:
+ for (i = 0 ; i < server->nclients ; i++) {
+ int inactive;
+ virMutexLock(&server->clients[i]->lock);
+ inactive = server->clients[i]->fd == -1
+ && server->clients[i]->refs == 0;
+ virMutexUnlock(&server->clients[i]->lock);
+ if (inactive) {
+ qemudFreeClient(server->clients[i]);
+ server->nclients--;
+ if (i < server->nclients)
+ memmove(server->clients + i,
+ server->clients + i + 1,
+ sizeof (*server->clients) * (server->nclients - i));
+
+ if (VIR_REALLOC_N(server->clients,
+ server->nclients) < 0) {
+ ; /* ignore */
+ }
+ goto reprocess;
+ }
+ }
+
+ /* If number of active workers exceeds both the min_workers
+ * threshold and the number of clients, then kill some
+ * off */
+ for (i = 0 ; (i < server->nworkers &&
+ server->nactiveworkers > server->nclients &&
+ server->nactiveworkers > min_workers) ; i++) {
+
+ if (server->workers[i].hasThread &&
+ !server->workers[i].processingCall) {
+ server->workers[i].quitRequest = 1;
+
+ virCondBroadcast(&server->job);
+ virMutexUnlock(&server->lock);
+ pthread_join(server->workers[i].thread, NULL);
+ virMutexLock(&server->lock);
+ server->workers[i].hasThread = 0;
+ server->nactiveworkers--;
+ }
+ }
+
+ if (server->shutdown) {
+ ret = 0;
+ break;
+ }
+ }
+
+cleanup:
+ for (i = 0 ; i < server->nworkers ; i++) {
+ if (!server->workers[i].hasThread)
+ continue;
+
+ server->workers[i].quitRequest = 1;
+ virCondBroadcast(&server->job);
+
+ virMutexUnlock(&server->lock);
+ pthread_join(server->workers[i].thread, NULL);
+ virMutexLock(&server->lock);
+ server->workers[i].hasThread = 0;
+ }
+ VIR_FREE(server->workers);
+
+ free(server->workers);
+ virMutexUnlock(&server->lock);
+ return ret;
+}
+
+static void qemudCleanup(struct qemud_server *server) {
+ struct qemud_socket *sock;
+
+ close(server->sigread);
+
+ sock = server->sockets;
+ while (sock) {
+ struct qemud_socket *next = sock->next;
+ close(sock->fd);
+ free(sock);
+ sock = next;
+ }
+ free(server->logDir);
+
+#ifdef HAVE_SASL
+ if (server->saslUsernameWhitelist) {
+ char **list = server->saslUsernameWhitelist;
+ while (*list) {
+ free(*list);
+ list++;
+ }
+ free(server->saslUsernameWhitelist);
+ }
+#endif
+
+ virStateCleanup();
+
+ if (virCondDestroy(&server->job) < 0) {
+ ;
+ }
+ virMutexDestroy(&server->lock);
+
+ VIR_FREE(server);
+}
+
+/* Allocate an array of malloc'd strings from the config file, filename
+ * (used only in diagnostics), using handle "conf". Upon error, return -1
+ * and free any allocated memory. Otherwise, save the array in *list_arg
+ * and return 0.
+ */
+static int
+remoteConfigGetStringList(virConfPtr conf, const char *key, char ***list_arg,
+ const char *filename)
+{
+ char **list;
+ virConfValuePtr p = virConfGetValue (conf, key);
+ if (!p)
+ return 0;
+
+ switch (p->type) {
+ case VIR_CONF_STRING:
+ if (VIR_ALLOC_N(list, 2) < 0) {
+ VIR_ERROR(_("failed to allocate memory for %s config list"), key);
+ return -1;
+ }
+ list[0] = strdup (p->str);
+ list[1] = NULL;
+ if (list[0] == NULL) {
+ VIR_ERROR(_("failed to allocate memory for %s config list value"),
+ key);
+ VIR_FREE(list);
+ return -1;
+ }
+ break;
+
+ case VIR_CONF_LIST: {
+ int i, len = 0;
+ virConfValuePtr pp;
+ for (pp = p->list; pp; pp = pp->next)
+ len++;
+ if (VIR_ALLOC_N(list, 1+len) < 0) {
+ VIR_ERROR(_("failed to allocate memory for %s config list"), key);
+ return -1;
+ }
+ for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
+ if (pp->type != VIR_CONF_STRING) {
+ VIR_ERROR(_("remoteReadConfigFile: %s: %s:"
+ " must be a string or list of strings\n"),
+ filename, key);
+ VIR_FREE(list);
+ return -1;
+ }
+ list[i] = strdup (pp->str);
+ if (list[i] == NULL) {
+ int j;
+ for (j = 0 ; j < i ; j++)
+ VIR_FREE(list[j]);
+ VIR_FREE(list);
+ VIR_ERROR(_("failed to allocate memory for %s config list value"),
+ key);
+ return -1;
+ }
+
+ }
+ list[i] = NULL;
+ break;
+ }
+
+ default:
+ VIR_ERROR(_("remoteReadConfigFile: %s: %s:"
+ " must be a string or list of strings\n"),
+ filename, key);
+ return -1;
+ }
+
+ *list_arg = list;
+ return 0;
+}
+
+/* A helper function used by each of the following macros. */
+static int
+checkType (virConfValuePtr p, const char *filename,
+ const char *key, virConfType required_type)
+{
+ if (p->type != required_type) {
+ VIR_ERROR(_("remoteReadConfigFile: %s: %s: invalid type:"
+ " got %s; expected %s\n"), filename, key,
+ virConfTypeName (p->type),
+ virConfTypeName (required_type));
+ return -1;
+ }
+ return 0;
+}
+
+/* If there is no config data for the key, #var_name, then do nothing.
+ If there is valid data of type VIR_CONF_STRING, and strdup succeeds,
+ store the result in var_name. Otherwise, (i.e. invalid type, or strdup
+ failure), give a diagnostic and "goto" the cleanup-and-fail label. */
+#define GET_CONF_STR(conf, filename, var_name) \
+ do { \
+ virConfValuePtr p = virConfGetValue (conf, #var_name); \
+ if (p) { \
+ if (checkType (p, filename, #var_name, VIR_CONF_STRING) < 0) \
+ goto free_and_fail; \
+ (var_name) = strdup (p->str); \
+ if ((var_name) == NULL) { \
+ char ebuf[1024]; \
+ VIR_ERROR(_("remoteReadConfigFile: %s\n"), \
+ virStrerror(errno, ebuf, sizeof ebuf)); \
+ goto free_and_fail; \
+ } \
+ } \
+ } while (0)
+
+/* Like GET_CONF_STR, but for integral values. */
+#define GET_CONF_INT(conf, filename, var_name) \
+ do { \
+ virConfValuePtr p = virConfGetValue (conf, #var_name); \
+ if (p) { \
+ if (checkType (p, filename, #var_name, VIR_CONF_LONG) < 0) \
+ goto free_and_fail; \
+ (var_name) = p->l; \
+ } \
+ } while (0)
+
+
+static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, const char *filename) {
+ virConfValuePtr p;
+
+ p = virConfGetValue (conf, key);
+ if (!p)
+ return 0;
+
+ if (checkType (p, filename, key, VIR_CONF_STRING) < 0)
+ return -1;
+
+ if (!p->str)
+ return 0;
+
+ if (STREQ(p->str, "none")) {
+ *auth = REMOTE_AUTH_NONE;
+#if HAVE_SASL
+ } else if (STREQ(p->str, "sasl")) {
+ *auth = REMOTE_AUTH_SASL;
+#endif
+#if HAVE_POLKIT
+ } else if (STREQ(p->str, "polkit")) {
+ *auth = REMOTE_AUTH_POLKIT;
+#endif
+ } else {
+ VIR_ERROR(_("remoteReadConfigFile: %s: %s: unsupported auth %s\n"),
+ filename, key, p->str);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_SASL
+static inline int
+remoteReadSaslAllowedUsernameList (virConfPtr conf,
+ struct qemud_server *server,
+ const char *filename)
+{
+ return
+ remoteConfigGetStringList (conf, "sasl_allowed_username_list",
+ &server->saslUsernameWhitelist, filename);
+}
+#else
+static inline int
+remoteReadSaslAllowedUsernameList (virConfPtr conf ATTRIBUTE_UNUSED,
+ struct qemud_server *server ATTRIBUTE_UNUSED,
+ const char *filename ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Set up the logging environment
+ * By default if daemonized all errors go to syslog and the logging
+ * is also saved onto the logfile libvird.log, but if verbose or error
+ * debugging is asked for then output informations or debug.
+ */
+static int
+qemudSetLogging(virConfPtr conf, const char *filename) {
+ int log_level;
+ char *log_filters = NULL;
+ char *log_outputs = NULL;
+ int ret = -1;
+
+ virLogReset();
+
+ /*
+ * Libvirtd's order of precedence is:
+ * cmdline > environment > config
+ *
+ * In order to achieve this, we must process configuration in
+ * different order for the log level versus the filters and
+ * outputs. Because filters and outputs append, we have to look at
+ * the environment first and then only check the config file if
+ * there was no result from the environment. The default output is
+ * then applied only if there was no setting from either of the
+ * first two. Because we don't have a way to determine if the log
+ * level has been set, we must process variables in the opposite
+ * order, each one overriding the previous.
+ */
+ /*
+ * GET_CONF_INT returns 0 when there is no log_level setting in
+ * the config file. The conditional below eliminates a false
+ * warning in that case, but also has the side effect of missing
+ * a warning if the user actually does say log_level=0.
+ */
+ GET_CONF_INT (conf, filename, log_level);
+ if (log_level != 0)
+ virLogSetDefaultPriority(log_level);
+
+ virLogSetFromEnv();
+
+ if (virLogGetNbFilters() == 0) {
+ GET_CONF_STR (conf, filename, log_filters);
+ virLogParseFilters(log_filters);
+ }
+
+ if (virLogGetNbOutputs() == 0) {
+ GET_CONF_STR (conf, filename, log_outputs);
+ virLogParseOutputs(log_outputs);
+ }
+
+ /*
+ * If no defined outputs, then direct to syslog when running
+ * as daemon. Otherwise the default output is stderr.
+ */
+ if (virLogGetNbOutputs() == 0) {
+ char *tmp = NULL;
+ if (godaemon) {
+ if (virAsprintf (&tmp, "%d:syslog:libvirtd",
+ virLogGetDefaultPriority()) < 0)
+ goto free_and_fail;
+ } else {
+ if (virAsprintf (&tmp, "%d:stderr",
+ virLogGetDefaultPriority()) < 0)
+ goto free_and_fail;
+ }
+ virLogParseOutputs(tmp);
+ VIR_FREE(tmp);
+ }
+
+ /*
+ * Command line override for --verbose
+ */
+ if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
+ virLogSetDefaultPriority(VIR_LOG_INFO);
+
+ ret = 0;
+
+free_and_fail:
+ VIR_FREE(log_filters);
+ VIR_FREE(log_outputs);
+ return(ret);
+}
+
+/* Read the config file if it exists.
+ * Only used in the remote case, hence the name.
+ */
+static int
+remoteReadConfigFile (struct qemud_server *server, const char *filename)
+{
+ virConfPtr conf;
+
+ /* The following variable names must match the corresponding
+ configuration strings. */
+ char *unix_sock_ro_perms = NULL;
+ char *unix_sock_rw_perms = NULL;
+ char *unix_sock_group = NULL;
+ char *buf = NULL;
+
+#if HAVE_POLKIT
+ /* Change the default back to no auth for non-root */
+ if (!server->privileged && auth_unix_rw == REMOTE_AUTH_POLKIT)
+ auth_unix_rw = REMOTE_AUTH_NONE;
+ if (!server->privileged && auth_unix_ro == REMOTE_AUTH_POLKIT)
+ auth_unix_ro = REMOTE_AUTH_NONE;
+#endif
+
+ conf = virConfReadFile (filename, 0);
+ if (!conf) return -1;
+
+ /*
+ * First get all the logging settings and activate them
+ */
+ if (qemudSetLogging(conf, filename) < 0)
+ goto free_and_fail;
+
+ GET_CONF_INT (conf, filename, listen_tcp);
+ GET_CONF_INT (conf, filename, listen_tls);
+ GET_CONF_STR (conf, filename, tls_port);
+ GET_CONF_STR (conf, filename, tcp_port);
+ GET_CONF_STR (conf, filename, listen_addr);
+
+ if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0)
+ goto free_and_fail;
+#if HAVE_POLKIT
+ /* Change default perms to be wide-open if PolicyKit is enabled.
+ * Admin can always override in config file
+ */
+ if (auth_unix_rw == REMOTE_AUTH_POLKIT)
+ unix_sock_rw_mask = 0777;
+#endif
+ if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0)
+ goto free_and_fail;
+ if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0)
+ goto free_and_fail;
+ if (remoteConfigGetAuth(conf, "auth_tls", &auth_tls, filename) < 0)
+ goto free_and_fail;
+
+ GET_CONF_STR (conf, filename, unix_sock_group);
+ if (unix_sock_group) {
+ if (!server->privileged) {
+ VIR_WARN0(_("Cannot set group when not running as root"));
+ } else {
+ int ret;
+ struct group grpdata, *grp;
+ size_t maxbuf = sysconf(_SC_GETGR_R_SIZE_MAX);
+
+ if (maxbuf == -1)
+ maxbuf = 1024;
+
+ if (VIR_ALLOC_N(buf, maxbuf) < 0) {
+ VIR_ERROR("%s", _("Failed to allocate memory for buffer"));
+ goto free_and_fail;
+ }
+
+ while ((ret = getgrnam_r(unix_sock_group, &grpdata,
+ buf, maxbuf,
+ &grp)) == ERANGE) {
+ maxbuf *= 2;
+ if (maxbuf > 65536 || VIR_REALLOC_N(buf, maxbuf) < 0) {
+ VIR_ERROR("%s", _("Failed to reallocate enough memory for buffer"));
+ goto free_and_fail;
+ }
+ }
+
+ if (ret != 0 || !grp) {
+ VIR_ERROR(_("Failed to lookup group '%s'"), unix_sock_group);
+ goto free_and_fail;
+ }
+ unix_sock_gid = grp->gr_gid;
+ VIR_FREE (buf);
+ }
+ free (unix_sock_group);
+ unix_sock_group = NULL;
+ }
+
+ GET_CONF_STR (conf, filename, unix_sock_ro_perms);
+ if (unix_sock_ro_perms) {
+ if (virStrToLong_i (unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) {
+ VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_ro_perms);
+ goto free_and_fail;
+ }
+ free (unix_sock_ro_perms);
+ unix_sock_ro_perms = NULL;
+ }
+
+ GET_CONF_STR (conf, filename, unix_sock_rw_perms);
+ if (unix_sock_rw_perms) {
+ if (virStrToLong_i (unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) {
+ VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_rw_perms);
+ goto free_and_fail;
+ }
+ free (unix_sock_rw_perms);
+ unix_sock_rw_perms = NULL;
+ }
+
+ GET_CONF_STR (conf, filename, unix_sock_dir);
+
+ GET_CONF_INT (conf, filename, mdns_adv);
+ GET_CONF_STR (conf, filename, mdns_name);
+
+ GET_CONF_INT (conf, filename, tls_no_verify_certificate);
+
+ GET_CONF_STR (conf, filename, key_file);
+ GET_CONF_STR (conf, filename, cert_file);
+ GET_CONF_STR (conf, filename, ca_file);
+ GET_CONF_STR (conf, filename, crl_file);
+
+ if (remoteConfigGetStringList (conf, "tls_allowed_dn_list",
+ &tls_allowed_dn_list, filename) < 0)
+ goto free_and_fail;
+
+ if (remoteReadSaslAllowedUsernameList (conf, server, filename) < 0)
+ goto free_and_fail;
+
+
+ GET_CONF_INT (conf, filename, min_workers);
+ GET_CONF_INT (conf, filename, max_workers);
+ GET_CONF_INT (conf, filename, max_clients);
+
+ GET_CONF_INT (conf, filename, max_requests);
+ GET_CONF_INT (conf, filename, max_client_requests);
+
+ virConfFree (conf);
+ return 0;
+
+ free_and_fail:
+ virConfFree (conf);
+ free (mdns_name);
+ mdns_name = NULL;
+ free (unix_sock_ro_perms);
+ free (unix_sock_rw_perms);
+ free (unix_sock_group);
+ VIR_FREE (buf);
+
+ /* Don't bother trying to free listen_addr, tcp_port, tls_port, key_file,
+ cert_file, ca_file, or crl_file, since they are initialized to
+ non-malloc'd strings. Besides, these are static variables, and callers
+ are unlikely to call this function more than once, so there wouldn't
+ even be a real leak. */
+
+ if (tls_allowed_dn_list) {
+ int i;
+ for (i = 0; tls_allowed_dn_list[i]; i++)
+ free (tls_allowed_dn_list[i]);
+ free (tls_allowed_dn_list);
+ tls_allowed_dn_list = NULL;
+ }
+
+ return -1;
+}
+
+/* Display version information. */
+static void
+version (const char *argv0)
+{
+ printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
+}
+
+#ifdef __sun
+static int
+qemudSetupPrivs (void)
+{
+ chown ("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID);
+
+ if (__init_daemon_priv (PU_RESETGROUPS | PU_CLEARLIMITSET,
+ SYSTEM_UID, SYSTEM_UID, PRIV_XVM_CONTROL, NULL)) {
+ VIR_ERROR0(_("additional privileges are required\n"));
+ return -1;
+ }
+
+ if (priv_set (PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO,
+ PRIV_PROC_SESSION, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL)) {
+ VIR_ERROR0(_("failed to set reduced privileges\n"));
+ return -1;
+ }
+
+ return 0;
+}
+#else
+#define qemudSetupPrivs() 0
+#endif
+
+/* Print command-line usage. */
+static void
+usage (const char *argv0)
+{
+ fprintf (stderr,
+ "\n\
+Usage:\n\
+ %s [options]\n\
+\n\
+Options:\n\
+ -v | --verbose Verbose messages.\n\
+ -d | --daemon Run as a daemon & write PID file.\n\
+ -l | --listen Listen for TCP/IP connections.\n\
+ -t | --timeout <secs> Exit after timeout period.\n\
+ -f | --config <file> Configuration file.\n\
+ | --version Display version information.\n\
+ -p | --pid-file <file> Change name of PID file.\n\
+\n\
+libvirt management daemon:\n\
+\n\
+ Default paths:\n\
+\n\
+ Configuration file (unless overridden by -c):\n\
+ " SYSCONF_DIR "/libvirt/libvirtd.conf\n\
+\n\
+ Sockets (as root):\n\
+ " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock\n\
+ " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock-ro\n\
+\n\
+ Sockets (as non-root):\n\
+ $HOME/.libvirt/libvirt-sock (in UNIX abstract namespace)\n\
+\n\
+ TLS:\n\
+ CA certificate: " LIBVIRT_CACERT "\n\
+ Server certificate: " LIBVIRT_SERVERCERT "\n\
+ Server private key: " LIBVIRT_SERVERKEY "\n\
+\n\
+ PID file (unless overridden by --pid-file):\n\
+ %s\n\
+\n",
+ argv0,
+ REMOTE_PID_FILE[0] != '\0'
+ ? REMOTE_PID_FILE
+ : "(disabled in ./configure)");
+}
+
+enum {
+ OPT_VERSION = 129
+};
+
+#define MAX_LISTEN 5
+int main(int argc, char **argv) {
+ struct qemud_server *server = NULL;
+ struct sigaction sig_action;
+ int sigpipe[2];
+ const char *pid_file = NULL;
+ const char *remote_config_file = NULL;
+ int ret = 1;
+
+ struct option opts[] = {
+ { "verbose", no_argument, &verbose, 1},
+ { "daemon", no_argument, &godaemon, 1},
+ { "listen", no_argument, &ipsock, 1},
+ { "config", required_argument, NULL, 'f'},
+ { "timeout", required_argument, NULL, 't'},
+ { "pid-file", required_argument, NULL, 'p'},
+ { "version", no_argument, NULL, OPT_VERSION },
+ { "help", no_argument, NULL, '?' },
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ int optidx = 0;
+ int c;
+ char *tmp;
+
+ c = getopt_long(argc, argv, "ldf:p:t:v", opts, &optidx);
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ /* Got one of the flags */
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'd':
+ godaemon = 1;
+ break;
+ case 'l':
+ ipsock = 1;
+ break;
+
+ case 't':
+ if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0
+ || timeout <= 0
+ /* Ensure that we can multiply by 1000 without overflowing. */
+ || timeout > INT_MAX / 1000)
+ timeout = -1;
+ break;
+
+ case 'p':
+ pid_file = optarg;
+ break;
+
+ case 'f':
+ remote_config_file = optarg;
+ break;
+
+ case OPT_VERSION:
+ version (argv[0]);
+ return 0;
+
+ case '?':
+ usage (argv[0]);
+ return 2;
+
+ default:
+ fprintf (stderr, "libvirtd: internal error: unknown flag: %c\n",
+ c);
+ exit (1);
+ }
+ }
+
+ if (remote_config_file == NULL) {
+ static const char *default_config_file
+ = SYSCONF_DIR "/libvirt/libvirtd.conf";
+ remote_config_file =
+ (access(default_config_file, R_OK) == 0
+ ? default_config_file
+ : "/dev/null");
+ }
+
+ if (godaemon) {
+ char ebuf[1024];
+ if (qemudGoDaemon() < 0) {
+ VIR_ERROR(_("Failed to fork as daemon: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ goto error1;
+ }
+ }
+
+ /* If running as root and no PID file is set, use the default */
+ if (pid_file == NULL &&
+ geteuid() == 0 &&
+ REMOTE_PID_FILE[0] != '\0')
+ pid_file = REMOTE_PID_FILE;
+
+ /* If we have a pidfile set, claim it now, exiting if already taken */
+ if (pid_file != NULL &&
+ qemudWritePidFile (pid_file) < 0)
+ goto error1;
+
+ if (pipe(sigpipe) < 0 ||
+ virSetNonBlock(sigpipe[0]) < 0 ||
+ virSetNonBlock(sigpipe[1]) < 0 ||
+ virSetCloseExec(sigpipe[0]) < 0 ||
+ virSetCloseExec(sigpipe[1]) < 0) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to create pipe: %s"),
+ virStrerror(errno, ebuf, sizeof ebuf));
+ goto error2;
+ }
+ sigwrite = sigpipe[1];
+
+ sig_action.sa_sigaction = sig_handler;
+ sig_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&sig_action.sa_mask);
+
+ sigaction(SIGHUP, &sig_action, NULL);
+ sigaction(SIGINT, &sig_action, NULL);
+ sigaction(SIGQUIT, &sig_action, NULL);
+ sigaction(SIGTERM, &sig_action, NULL);
+ sigaction(SIGCHLD, &sig_action, NULL);
+
+ sig_action.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sig_action, NULL);
+
+ /* Ensure the rundir exists (on tmpfs on some systems) */
+ if (geteuid() == 0) {
+ const char *rundir = LOCAL_STATE_DIR "/run/libvirt";
+
+ if (mkdir (rundir, 0755)) {
+ if (errno != EEXIST) {
+ VIR_ERROR0 (_("unable to create rundir"));
+ return -1;
+ }
+ }
+ }
+
+ /* Beyond this point, nothing should rely on using
+ * getuid/geteuid() == 0, for privilege level checks.
+ * It must all use the flag 'server->privileged'
+ * which is also passed into all libvirt stateful
+ * drivers
+ */
+ if (qemudSetupPrivs() < 0)
+ goto error2;
+
+ if (!(server = qemudInitialize(sigpipe[0]))) {
+ ret = 2;
+ goto error2;
+ }
+
+ /* Read the config file (if it exists). */
+ if (remoteReadConfigFile (server, remote_config_file) < 0)
+ goto error2;
+
+ /* Change the group ownership of /var/run/libvirt to unix_sock_gid */
+ if (unix_sock_dir && server->privileged) {
+ if (chown(unix_sock_dir, -1, unix_sock_gid) < 0)
+ VIR_ERROR(_("Failed to change group ownership of %s"),
+ unix_sock_dir);
+ }
+
+ if (virEventAddHandleImpl(sigpipe[0],
+ VIR_EVENT_HANDLE_READABLE,
+ qemudDispatchSignalEvent,
+ server, NULL) < 0) {
+ VIR_ERROR0(_("Failed to register callback for signal pipe"));
+ ret = 3;
+ goto error2;
+ }
+
+ if (!(server = qemudNetworkInit(server))) {
+ ret = 2;
+ goto error2;
+ }
+
+ qemudRunLoop(server);
+
+ ret = 0;
+
+error2:
+ if (server)
+ qemudCleanup(server);
+ if (pid_file)
+ unlink (pid_file);
+ close(sigwrite);
+
+error1:
+ virLogShutdown();
+ return ret;
+}
--- /dev/null
+/*
+ * libvirtd.h: daemon data structure definitions
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef QEMUD_INTERNAL_H__
+#define QEMUD_INTERNAL_H__
+
+#include <config.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "gnutls_1_0_compat.h"
+#if HAVE_SASL
+#include <sasl/sasl.h>
+#endif
+
+#if HAVE_POLKIT0
+#include <dbus/dbus.h>
+#endif
+
+#ifdef HAVE_SYS_SYSLIMITS_H
+#include <sys/syslimits.h>
+#endif
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include "remote_protocol.h"
+#include "logging.h"
+#include "threads.h"
+
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include <ansidecl.h>
+#endif
+
+#ifndef __GNUC_PREREQ
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+#define __GNUC_PREREQ(maj,min) 0
+#endif
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * Macro to flag conciously unused parameters to functions
+ */
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#endif
+
+/**
+ * ATTRIBUTE_FMT_PRINTF
+ *
+ * Macro used to check printf like functions, if compiling
+ * with gcc.
+ *
+ * We use gnulib which guarentees we always have GNU style
+ * printf format specifiers even on broken Win32 platforms
+ * hence we have to force 'gnu_printf' for new GCC
+ */
+#ifndef ATTRIBUTE_FMT_PRINTF
+#if __GNUC_PREREQ (4, 4)
+#define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (gnu_printf, fmtpos,argpos)))
+#else
+#define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (printf, fmtpos,argpos)))
+#endif
+#endif
+
+#ifndef ATTRIBUTE_RETURN_CHECK
+#if __GNUC_PREREQ (3, 4)
+#define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__))
+#else
+#define ATTRIBUTE_RETURN_CHECK
+#endif
+#endif
+
+#else
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED
+#endif
+#ifndef ATTRIBUTE_FMT_PRINTF
+#define ATTRIBUTE_FMT_PRINTF(...)
+#endif
+#ifndef ATTRIBUTE_RETURN_CHECK
+#define ATTRIBUTE_RETURN_CHECK
+#endif
+#endif
+
+#define qemudDebug DEBUG
+
+/* Whether we're passing reads & writes through a sasl SSF */
+enum qemud_sasl_ssf {
+ QEMUD_SASL_SSF_NONE = 0,
+ QEMUD_SASL_SSF_READ = 1,
+ QEMUD_SASL_SSF_WRITE = 2,
+};
+
+enum qemud_sock_type {
+ QEMUD_SOCK_TYPE_UNIX = 0,
+ QEMUD_SOCK_TYPE_TCP = 1,
+ QEMUD_SOCK_TYPE_TLS = 2,
+};
+
+struct qemud_client_message {
+ char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN];
+ unsigned int bufferLength;
+ unsigned int bufferOffset;
+
+ int async : 1;
+
+ remote_message_header hdr;
+
+ struct qemud_client_message *next;
+};
+
+/* Allow for filtering of incoming messages to a custom
+ * dispatch processing queue, instead of client->dx.
+ */
+typedef int (*qemud_client_filter_func)(struct qemud_client_message *msg, void *opaque);
+struct qemud_client_filter {
+ qemud_client_filter_func query;
+ void *opaque;
+
+ struct qemud_client_message *dx;
+
+ struct qemud_client_filter *next;
+};
+
+/* Stores the per-client connection state */
+struct qemud_client {
+ virMutex lock;
+
+ int magic;
+
+ int fd;
+ int watch;
+ int readonly:1;
+ int closing:1;
+
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+
+ int type; /* qemud_sock_type */
+ gnutls_session_t tlssession;
+ int auth;
+ int handshake : 1; /* If we're in progress for TLS handshake */
+#if HAVE_SASL
+ sasl_conn_t *saslconn;
+ int saslSSF;
+ const char *saslDecoded;
+ unsigned int saslDecodedLength;
+ unsigned int saslDecodedOffset;
+ const char *saslEncoded;
+ unsigned int saslEncodedLength;
+ unsigned int saslEncodedOffset;
+ char *saslUsername;
+#endif
+
+ /* Count of meages in 'dx' or 'tx' queue
+ * ie RPC calls in progress. Does not count
+ * async events which are not used for
+ * throttling calculations */
+ int nrequests;
+ /* Zero or one messages being received. Zero if
+ * nrequests >= max_clients and throttling */
+ struct qemud_client_message *rx;
+ /* Zero or many messages waiting for a worker
+ * to process them */
+ struct qemud_client_message *dx;
+ /* Zero or many messages waiting for transmit
+ * back to client, including async events */
+ struct qemud_client_message *tx;
+ /* Filters to capture messages that would otherwise
+ * end up on the 'dx' queue */
+ struct qemud_client_filter *filters;
+
+ /* This is only valid if a remote open call has been made on this
+ * connection, otherwise it will be NULL. Also if remote close is
+ * called, it will be set back to NULL if that succeeds.
+ */
+ virConnectPtr conn;
+ int refs;
+
+};
+
+#define QEMUD_CLIENT_MAGIC 0x7788aaee
+
+
+struct qemud_socket {
+ int fd;
+ int watch;
+ int readonly;
+ int type; /* qemud_sock_type */
+ int auth;
+ int port;
+ struct qemud_socket *next;
+};
+
+struct qemud_worker {
+ pthread_t thread;
+ int hasThread :1;
+ int processingCall :1;
+ int quitRequest : 1;
+
+ /* back-pointer to our server */
+ struct qemud_server *server;
+};
+
+/* Main server state */
+struct qemud_server {
+ virMutex lock;
+ virCond job;
+
+ int privileged;
+
+ int nworkers;
+ int nactiveworkers;
+ struct qemud_worker *workers;
+ int nsockets;
+ struct qemud_socket *sockets;
+ int nclients;
+ struct qemud_client **clients;
+
+ int sigread;
+ char *logDir;
+ unsigned int shutdown : 1;
+#ifdef HAVE_AVAHI
+ struct libvirtd_mdns *mdns;
+#endif
+#if HAVE_SASL
+ char **saslUsernameWhitelist;
+#endif
+#if HAVE_POLKIT0
+ DBusConnection *sysbus;
+#endif
+};
+
+void qemudLog(int priority, const char *fmt, ...)
+ ATTRIBUTE_FMT_PRINTF(2,3);
+
+
+
+int qemudRegisterClientEvent(struct qemud_server *server,
+ struct qemud_client *client);
+void qemudUpdateClientEvent(struct qemud_client *client);
+
+void qemudDispatchClientFailure(struct qemud_client *client);
+
+void
+qemudClientMessageQueuePush(struct qemud_client_message **queue,
+ struct qemud_client_message *msg);
+struct qemud_client_message *
+qemudClientMessageQueueServe(struct qemud_client_message **queue);
+
+
+
+#if HAVE_POLKIT
+int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid);
+#endif
+
+#endif
#include <avahi-common/error.h>
#include <avahi-common/timeval.h>
-#include "internal.h"
-#include "qemud.h"
+#include "libvirtd.h"
#include "mdns.h"
#include "event.h"
#include "memory.h"
+++ /dev/null
-/*
- * qemud.c: daemon start of day, guest process & i/o management
- *
- * Copyright (C) 2006, 2007, 2008, 2009 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/poll.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <netdb.h>
-#include <stdlib.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <syslog.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-#include <fnmatch.h>
-#include <grp.h>
-#include <signal.h>
-#include <netdb.h>
-
-#include "libvirt_internal.h"
-#include "virterror_internal.h"
-
-#define VIR_FROM_THIS VIR_FROM_QEMU
-
-#include "qemud.h"
-#include "dispatch.h"
-
-#include "util.h"
-#include "remote/remote_driver.h"
-#include "conf.h"
-#include "event.h"
-#include "memory.h"
-#ifdef HAVE_AVAHI
-#include "mdns.h"
-#endif
-
-#ifdef WITH_DRIVER_MODULES
-#include "driver.h"
-#else
-#ifdef WITH_QEMU
-#include "qemu/qemu_driver.h"
-#endif
-#ifdef WITH_LXC
-#include "lxc/lxc_driver.h"
-#endif
-#ifdef WITH_UML
-#include "uml/uml_driver.h"
-#endif
-#ifdef WITH_ONE
-#include "opennebula/one_driver.h"
-#endif
-#ifdef WITH_NETWORK
-#include "network/bridge_driver.h"
-#endif
-#ifdef WITH_NETCF
-#include "interface/netcf_driver.h"
-#endif
-#ifdef WITH_STORAGE_DIR
-#include "storage/storage_driver.h"
-#endif
-#ifdef WITH_NODE_DEVICES
-#include "node_device/node_device_driver.h"
-#endif
-#include "secret/secret_driver.h"
-#endif
-
-
-#ifdef __sun
-#include <ucred.h>
-#include <priv.h>
-
-#ifndef PRIV_VIRT_MANAGE
-#define PRIV_VIRT_MANAGE ((const char *)"virt_manage")
-#endif
-
-#ifndef PRIV_XVM_CONTROL
-#define PRIV_XVM_CONTROL ((const char *)"xvm_control")
-#endif
-
-#define PU_RESETGROUPS 0x0001 /* Remove supplemental groups */
-#define PU_CLEARLIMITSET 0x0008 /* L=0 */
-
-extern int __init_daemon_priv(int, uid_t, gid_t, ...);
-
-#define SYSTEM_UID 60
-
-static gid_t unix_sock_gid = 60; /* Not used */
-static int unix_sock_rw_mask = 0666;
-static int unix_sock_ro_mask = 0666;
-
-#else
-
-static gid_t unix_sock_gid = 0; /* Only root by default */
-static int unix_sock_rw_mask = 0700; /* Allow user only */
-static int unix_sock_ro_mask = 0777; /* Allow world */
-
-#endif /* __sun */
-
-static int godaemon = 0; /* -d: Be a daemon */
-static int verbose = 0; /* -v: Verbose mode */
-static int timeout = -1; /* -t: Shutdown timeout */
-static int sigwrite = -1; /* Signal handler pipe */
-static int ipsock = 0; /* -l Listen for TCP/IP */
-
-/* Defaults for configuration file elements */
-static int listen_tls = 1;
-static int listen_tcp = 0;
-static char *listen_addr = (char *) LIBVIRTD_LISTEN_ADDR;
-static char *tls_port = (char *) LIBVIRTD_TLS_PORT;
-static char *tcp_port = (char *) LIBVIRTD_TCP_PORT;
-
-static char *unix_sock_dir = NULL;
-
-#if HAVE_POLKIT
-static int auth_unix_rw = REMOTE_AUTH_POLKIT;
-static int auth_unix_ro = REMOTE_AUTH_POLKIT;
-#else
-static int auth_unix_rw = REMOTE_AUTH_NONE;
-static int auth_unix_ro = REMOTE_AUTH_NONE;
-#endif /* HAVE_POLKIT */
-#if HAVE_SASL
-static int auth_tcp = REMOTE_AUTH_SASL;
-#else
-static int auth_tcp = REMOTE_AUTH_NONE;
-#endif
-static int auth_tls = REMOTE_AUTH_NONE;
-
-static int mdns_adv = 1;
-static char *mdns_name = NULL;
-
-static int tls_no_verify_certificate = 0;
-static char **tls_allowed_dn_list = NULL;
-
-static char *key_file = (char *) LIBVIRT_SERVERKEY;
-static char *cert_file = (char *) LIBVIRT_SERVERCERT;
-static char *ca_file = (char *) LIBVIRT_CACERT;
-static char *crl_file = (char *) "";
-
-static gnutls_certificate_credentials_t x509_cred;
-static gnutls_dh_params_t dh_params;
-
-static int min_workers = 5;
-static int max_workers = 20;
-static int max_clients = 20;
-
-/* Total number of 'in-process' RPC calls allowed across all clients */
-static int max_requests = 20;
-/* Total number of 'in-process' RPC calls allowed by a single client*/
-static int max_client_requests = 5;
-
-#define DH_BITS 1024
-
-static sig_atomic_t sig_errors = 0;
-static int sig_lasterrno = 0;
-
-static void sig_handler(int sig, siginfo_t * siginfo,
- void* context ATTRIBUTE_UNUSED) {
- int origerrno;
- int r;
-
- /* set the sig num in the struct */
- siginfo->si_signo = sig;
-
- origerrno = errno;
- r = safewrite(sigwrite, siginfo, sizeof(*siginfo));
- if (r == -1) {
- sig_errors++;
- sig_lasterrno = errno;
- }
- errno = origerrno;
-}
-
-static void qemudDispatchClientEvent(int watch, int fd, int events, void *opaque);
-static void qemudDispatchServerEvent(int watch, int fd, int events, void *opaque);
-static int qemudStartWorker(struct qemud_server *server, struct qemud_worker *worker);
-
-void
-qemudClientMessageQueuePush(struct qemud_client_message **queue,
- struct qemud_client_message *msg)
-{
- struct qemud_client_message *tmp = *queue;
-
- if (tmp) {
- while (tmp->next)
- tmp = tmp->next;
- tmp->next = msg;
- } else {
- *queue = msg;
- }
-}
-
-struct qemud_client_message *
-qemudClientMessageQueueServe(struct qemud_client_message **queue)
-{
- struct qemud_client_message *tmp = *queue;
-
- if (tmp) {
- *queue = tmp->next;
- tmp->next = NULL;
- }
-
- return tmp;
-}
-
-static int
-remoteCheckCertFile(const char *type, const char *file)
-{
- struct stat sb;
- if (stat(file, &sb) < 0) {
- char ebuf[1024];
- VIR_ERROR(_("Cannot access %s '%s': %s"),
- type, file, virStrerror(errno, ebuf, sizeof ebuf));
- return -1;
- }
- return 0;
-}
-
-static int
-remoteInitializeGnuTLS (void)
-{
- int err;
-
- /* Initialise GnuTLS. */
- gnutls_global_init ();
-
- err = gnutls_certificate_allocate_credentials (&x509_cred);
- if (err) {
- VIR_ERROR(_("gnutls_certificate_allocate_credentials: %s"),
- gnutls_strerror (err));
- return -1;
- }
-
- if (ca_file && ca_file[0] != '\0') {
- if (remoteCheckCertFile("CA certificate", ca_file) < 0)
- return -1;
-
- qemudDebug ("loading CA cert from %s", ca_file);
- err = gnutls_certificate_set_x509_trust_file (x509_cred, ca_file,
- GNUTLS_X509_FMT_PEM);
- if (err < 0) {
- VIR_ERROR(_("gnutls_certificate_set_x509_trust_file: %s"),
- gnutls_strerror (err));
- return -1;
- }
- }
-
- if (crl_file && crl_file[0] != '\0') {
- if (remoteCheckCertFile("CA revocation list", crl_file) < 0)
- return -1;
-
- DEBUG("loading CRL from %s", crl_file);
- err = gnutls_certificate_set_x509_crl_file (x509_cred, crl_file,
- GNUTLS_X509_FMT_PEM);
- if (err < 0) {
- VIR_ERROR(_("gnutls_certificate_set_x509_crl_file: %s"),
- gnutls_strerror (err));
- return -1;
- }
- }
-
- if (cert_file && cert_file[0] != '\0' && key_file && key_file[0] != '\0') {
- if (remoteCheckCertFile("server certificate", cert_file) < 0)
- return -1;
- if (remoteCheckCertFile("server key", key_file) < 0)
- return -1;
- DEBUG("loading cert and key from %s and %s", cert_file, key_file);
- err =
- gnutls_certificate_set_x509_key_file (x509_cred,
- cert_file, key_file,
- GNUTLS_X509_FMT_PEM);
- if (err < 0) {
- VIR_ERROR(_("gnutls_certificate_set_x509_key_file: %s"),
- gnutls_strerror (err));
- return -1;
- }
- }
-
- /* Generate Diffie Hellman parameters - for use with DHE
- * kx algorithms. These should be discarded and regenerated
- * once a day, once a week or once a month. Depending on the
- * security requirements.
- */
- err = gnutls_dh_params_init (&dh_params);
- if (err < 0) {
- VIR_ERROR(_("gnutls_dh_params_init: %s"), gnutls_strerror (err));
- return -1;
- }
- err = gnutls_dh_params_generate2 (dh_params, DH_BITS);
- if (err < 0) {
- VIR_ERROR(_("gnutls_dh_params_generate2: %s"), gnutls_strerror (err));
- return -1;
- }
-
- gnutls_certificate_set_dh_params (x509_cred, dh_params);
-
- return 0;
-}
-
-static void
-qemudDispatchSignalEvent(int watch ATTRIBUTE_UNUSED,
- int fd ATTRIBUTE_UNUSED,
- int events ATTRIBUTE_UNUSED,
- void *opaque) {
- struct qemud_server *server = (struct qemud_server *)opaque;
- siginfo_t siginfo;
- int ret;
-
- virMutexLock(&server->lock);
-
- if (saferead(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to read from signal pipe: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- virMutexUnlock(&server->lock);
- return;
- }
-
- ret = 0;
-
- switch (siginfo.si_signo) {
- case SIGHUP:
- VIR_INFO0(_("Reloading configuration on SIGHUP"));
- if (virStateReload() < 0)
- VIR_WARN0(_("Error while reloading drivers"));
- break;
-
- case SIGINT:
- case SIGQUIT:
- case SIGTERM:
- VIR_WARN(_("Shutting down on signal %d"), siginfo.si_signo);
- server->shutdown = 1;
- break;
-
- default:
- VIR_INFO(_("Received unexpected signal %d"), siginfo.si_signo);
- break;
- }
-
- if (ret != 0)
- server->shutdown = 1;
-
- virMutexUnlock(&server->lock);
-}
-
-
-static int qemudGoDaemon(void) {
- int pid = fork();
- switch (pid) {
- case 0:
- {
- int stdinfd = -1;
- int stdoutfd = -1;
- int nextpid;
-
- if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
- goto cleanup;
- if ((stdoutfd = open("/dev/null", O_WRONLY)) < 0)
- goto cleanup;
- if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
- goto cleanup;
- if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
- goto cleanup;
- if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
- goto cleanup;
- if (close(stdinfd) < 0)
- goto cleanup;
- stdinfd = -1;
- if (close(stdoutfd) < 0)
- goto cleanup;
- stdoutfd = -1;
-
- if (setsid() < 0)
- goto cleanup;
-
- nextpid = fork();
- switch (nextpid) {
- case 0:
- return 0;
- case -1:
- return -1;
- default:
- _exit(0);
- }
-
- cleanup:
- if (stdoutfd != -1)
- close(stdoutfd);
- if (stdinfd != -1)
- close(stdinfd);
- return -1;
-
- }
-
- case -1:
- return -1;
-
- default:
- {
- int got, status = 0;
- /* We wait to make sure the next child forked
- successfully */
- if ((got = waitpid(pid, &status, 0)) < 0 ||
- got != pid ||
- status != 0) {
- return -1;
- }
- _exit(0);
- }
- }
-}
-
-static int qemudWritePidFile(const char *pidFile) {
- int fd;
- FILE *fh;
- char ebuf[1024];
-
- if (pidFile[0] == '\0')
- return 0;
-
- if ((fd = open(pidFile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
- VIR_ERROR(_("Failed to open pid file '%s' : %s"),
- pidFile, virStrerror(errno, ebuf, sizeof ebuf));
- return -1;
- }
-
- if (!(fh = fdopen(fd, "w"))) {
- VIR_ERROR(_("Failed to fdopen pid file '%s' : %s"),
- pidFile, virStrerror(errno, ebuf, sizeof ebuf));
- close(fd);
- return -1;
- }
-
- if (fprintf(fh, "%lu\n", (unsigned long)getpid()) < 0) {
- VIR_ERROR(_("Failed to write to pid file '%s' : %s"),
- pidFile, virStrerror(errno, ebuf, sizeof ebuf));
- fclose(fh);
- return -1;
- }
-
- if (fclose(fh) == EOF) {
- VIR_ERROR(_("Failed to close pid file '%s' : %s"),
- pidFile, virStrerror(errno, ebuf, sizeof ebuf));
- return -1;
- }
-
- return 0;
-}
-
-static int qemudListenUnix(struct qemud_server *server,
- const char *path, int readonly, int auth) {
- struct qemud_socket *sock;
- struct sockaddr_un addr;
- mode_t oldmask;
- gid_t oldgrp;
- char ebuf[1024];
-
- if (VIR_ALLOC(sock) < 0) {
- VIR_ERROR("%s", _("Failed to allocate memory for struct qemud_socket"));
- return -1;
- }
-
- sock->readonly = readonly;
- sock->port = -1;
- sock->type = QEMUD_SOCK_TYPE_UNIX;
- sock->auth = auth;
-
- if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
- VIR_ERROR(_("Failed to create socket: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- goto cleanup;
- }
-
- if (virSetCloseExec(sock->fd) < 0 ||
- virSetNonBlock(sock->fd) < 0)
- goto cleanup;
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
- if (addr.sun_path[0] == '@')
- addr.sun_path[0] = '\0';
-
-
- oldgrp = getgid();
- oldmask = umask(readonly ? ~unix_sock_ro_mask : ~unix_sock_rw_mask);
- if (server->privileged)
- setgid(unix_sock_gid);
-
- if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- VIR_ERROR(_("Failed to bind socket to '%s': %s"),
- path, virStrerror(errno, ebuf, sizeof ebuf));
- goto cleanup;
- }
- umask(oldmask);
- if (server->privileged)
- setgid(oldgrp);
-
- if (listen(sock->fd, 30) < 0) {
- VIR_ERROR(_("Failed to listen for connections on '%s': %s"),
- path, virStrerror(errno, ebuf, sizeof ebuf));
- goto cleanup;
- }
-
- if ((sock->watch = virEventAddHandleImpl(sock->fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchServerEvent,
- server, NULL)) < 0) {
- VIR_ERROR0(_("Failed to add server event callback"));
- goto cleanup;
- }
-
- sock->next = server->sockets;
- server->sockets = sock;
- server->nsockets++;
-
- return 0;
-
- cleanup:
- if (sock->fd)
- close(sock->fd);
- free(sock);
- return -1;
-}
-
-// See: http://people.redhat.com/drepper/userapi-ipv6.html
-static int
-remoteMakeSockets (int *fds, int max_fds, int *nfds_r, const char *node, const char *service)
-{
- struct addrinfo *ai;
- struct addrinfo hints;
- memset (&hints, 0, sizeof hints);
- hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
- hints.ai_socktype = SOCK_STREAM;
-
- int e = getaddrinfo (node, service, &hints, &ai);
- if (e != 0) {
- VIR_ERROR(_("getaddrinfo: %s\n"), gai_strerror (e));
- return -1;
- }
-
- struct addrinfo *runp = ai;
- while (runp && *nfds_r < max_fds) {
- char ebuf[1024];
- fds[*nfds_r] = socket (runp->ai_family, runp->ai_socktype,
- runp->ai_protocol);
- if (fds[*nfds_r] == -1) {
- VIR_ERROR(_("socket: %s"), virStrerror (errno, ebuf, sizeof ebuf));
- return -1;
- }
-
- int opt = 1;
- setsockopt (fds[*nfds_r], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
-
- if (bind (fds[*nfds_r], runp->ai_addr, runp->ai_addrlen) == -1) {
- if (errno != EADDRINUSE) {
- VIR_ERROR(_("bind: %s"), virStrerror (errno, ebuf, sizeof ebuf));
- return -1;
- }
- close (fds[*nfds_r]);
- }
- else {
- if (listen (fds[*nfds_r], SOMAXCONN) == -1) {
- VIR_ERROR(_("listen: %s"),
- virStrerror (errno, ebuf, sizeof ebuf));
- return -1;
- }
- ++*nfds_r;
- }
- runp = runp->ai_next;
- }
-
- freeaddrinfo (ai);
- return 0;
-}
-
-/* Listen on the named/numbered TCP port. On a machine with IPv4 and
- * IPv6 interfaces this may generate several sockets.
- */
-static int
-remoteListenTCP (struct qemud_server *server,
- const char *addr,
- const char *port,
- int type,
- int auth)
-{
- int fds[2];
- int nfds = 0;
- int i;
- struct qemud_socket *sock;
-
- if (remoteMakeSockets (fds, 2, &nfds, addr, port) == -1)
- return -1;
-
- for (i = 0; i < nfds; ++i) {
- union {
- struct sockaddr_storage sa_stor;
- struct sockaddr sa;
- struct sockaddr_in sa_in;
-#ifdef AF_INET6
- struct sockaddr_in6 sa_in6;
-#endif
- } s;
- char ebuf[1024];
- socklen_t salen = sizeof(s);
-
- if (VIR_ALLOC(sock) < 0) {
- VIR_ERROR(_("remoteListenTCP: calloc: %s"),
- virStrerror (errno, ebuf, sizeof ebuf));
- goto cleanup;
- }
-
- sock->readonly = 0;
- sock->next = server->sockets;
- server->sockets = sock;
- server->nsockets++;
-
- sock->fd = fds[i];
- sock->type = type;
- sock->auth = auth;
-
- if (getsockname(sock->fd, &s.sa, &salen) < 0)
- goto cleanup;
-
- if (s.sa.sa_family == AF_INET) {
- sock->port = htons(s.sa_in.sin_port);
-#ifdef AF_INET6
- } else if (s.sa.sa_family == AF_INET6)
- sock->port = htons(s.sa_in6.sin6_port);
-#endif
- else
- sock->port = -1;
-
- if (virSetCloseExec(sock->fd) < 0 ||
- virSetNonBlock(sock->fd) < 0)
- goto cleanup;
-
- if (listen (sock->fd, 30) < 0) {
- VIR_ERROR(_("remoteListenTCP: listen: %s"),
- virStrerror (errno, ebuf, sizeof ebuf));
- goto cleanup;
- }
-
- if ((sock->watch = virEventAddHandleImpl(sock->fd,
- VIR_EVENT_HANDLE_READABLE |
- VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP,
- qemudDispatchServerEvent,
- server, NULL)) < 0) {
- VIR_ERROR0(_("Failed to add server event callback"));
- goto cleanup;
- }
-
- }
-
- return 0;
-
-cleanup:
- for (i = 0; i < nfds; ++i)
- close(fds[i]);
- return -1;
-}
-
-static int qemudInitPaths(struct qemud_server *server,
- char *sockname,
- char *roSockname,
- int maxlen)
-{
- char *sock_dir;
- char *dir_prefix = NULL;
- int ret = -1;
- char *sock_dir_prefix = NULL;
-
- if (unix_sock_dir)
- sock_dir = unix_sock_dir;
- else {
- sock_dir = sockname;
- if (server->privileged) {
- dir_prefix = strdup (LOCAL_STATE_DIR);
- if (dir_prefix == NULL) {
- virReportOOMError(NULL);
- goto cleanup;
- }
- if (snprintf (sock_dir, maxlen, "%s/run/libvirt",
- dir_prefix) >= maxlen)
- goto snprintf_error;
- } else {
- uid_t uid = geteuid();
- dir_prefix = virGetUserDirectory(NULL, uid);
- if (dir_prefix == NULL) {
- /* Do not diagnose here; virGetUserDirectory does that. */
- goto snprintf_error;
- }
-
- if (snprintf(sock_dir, maxlen, "%s/.libvirt", dir_prefix) >= maxlen)
- goto snprintf_error;
- }
- }
-
- sock_dir_prefix = strdup (sock_dir);
- if (!sock_dir_prefix) {
- virReportOOMError(NULL);
- goto cleanup;
- }
-
- if (server->privileged) {
- if (snprintf (sockname, maxlen, "%s/libvirt-sock",
- sock_dir_prefix) >= maxlen
- || (snprintf (roSockname, maxlen, "%s/libvirt-sock-ro",
- sock_dir_prefix) >= maxlen))
- goto snprintf_error;
- unlink(sockname);
- unlink(roSockname);
- } else {
- if (snprintf(sockname, maxlen, "@%s/libvirt-sock",
- sock_dir_prefix) >= maxlen)
- goto snprintf_error;
- }
-
- if (server->privileged)
- server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt");
- else
- virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix);
-
- if (server->logDir == NULL)
- virReportOOMError(NULL);
-
- ret = 0;
-
- snprintf_error:
- if (ret)
- VIR_ERROR("%s",
- _("Resulting path too long for buffer in qemudInitPaths()"));
-
- cleanup:
- free (dir_prefix);
- free (sock_dir_prefix);
- return ret;
-}
-
-static struct qemud_server *qemudInitialize(int sigread) {
- struct qemud_server *server;
-
- if (VIR_ALLOC(server) < 0) {
- VIR_ERROR0(_("Failed to allocate struct qemud_server"));
- return NULL;
- }
-
- if (virMutexInit(&server->lock) < 0) {
- VIR_ERROR("%s", _("cannot initialize mutex"));
- VIR_FREE(server);
- }
- if (virCondInit(&server->job) < 0) {
- VIR_ERROR("%s", _("cannot initialize condition variable"));
- virMutexDestroy(&server->lock);
- VIR_FREE(server);
- }
-
- server->privileged = geteuid() == 0 ? 1 : 0;
- server->sigread = sigread;
-
- if (virEventInit() < 0) {
- VIR_ERROR0(_("Failed to initialize event system"));
- VIR_FREE(server);
- return NULL;
- }
-
- virInitialize();
-
- /*
- * Note that the order is important: the first ones have a higher
- * priority when calling virStateInitialize. We must register
- * the network, storage and nodedev drivers before any domain
- * drivers, since their resources must be auto-started before
- * any domains can be auto-started.
- */
-#ifdef WITH_DRIVER_MODULES
- /* We don't care if any of these fail, because the whole point
- * is to allow users to only install modules they want to use.
- * If they try to use a open a connection for a module that
- * is not loaded they'll get a suitable error at that point
- */
- virDriverLoadModule("network");
- virDriverLoadModule("storage");
- virDriverLoadModule("nodedev");
- virDriverLoadModule("secret");
- virDriverLoadModule("qemu");
- virDriverLoadModule("lxc");
- virDriverLoadModule("uml");
- virDriverLoadModule("one");
-#else
-#ifdef WITH_NETWORK
- networkRegister();
-#endif
-#ifdef WITH_NETCF
- interfaceRegister();
-#endif
-#ifdef WITH_STORAGE_DIR
- storageRegister();
-#endif
-#if defined(WITH_NODE_DEVICES) && \
- (defined(HAVE_HAL) || defined(HAVE_DEVKIT))
- nodedevRegister();
-#endif
- secretRegister();
-#ifdef WITH_QEMU
- qemuRegister();
-#endif
-#ifdef WITH_LXC
- lxcRegister();
-#endif
-#ifdef WITH_UML
- umlRegister();
-#endif
-#ifdef WITH_ONE
- oneRegister();
-#endif
-#endif
-
- virEventRegisterImpl(virEventAddHandleImpl,
- virEventUpdateHandleImpl,
- virEventRemoveHandleImpl,
- virEventAddTimeoutImpl,
- virEventUpdateTimeoutImpl,
- virEventRemoveTimeoutImpl);
-
- virStateInitialize(server->privileged);
-
- return server;
-}
-
-static struct qemud_server *qemudNetworkInit(struct qemud_server *server) {
- struct qemud_socket *sock;
- char sockname[PATH_MAX];
- char roSockname[PATH_MAX];
-#if HAVE_SASL
- int err;
-#endif /* HAVE_SASL */
-
- roSockname[0] = '\0';
-
- if (qemudInitPaths(server, sockname, roSockname, PATH_MAX) < 0)
- goto cleanup;
-
- if (qemudListenUnix(server, sockname, 0, auth_unix_rw) < 0)
- goto cleanup;
-
- if (roSockname[0] != '\0' && qemudListenUnix(server, roSockname, 1, auth_unix_ro) < 0)
- goto cleanup;
-
-#if HAVE_SASL
- if (auth_unix_rw == REMOTE_AUTH_SASL ||
- auth_unix_ro == REMOTE_AUTH_SASL ||
- auth_tcp == REMOTE_AUTH_SASL ||
- auth_tls == REMOTE_AUTH_SASL) {
- if ((err = sasl_server_init(NULL, "libvirt")) != SASL_OK) {
- VIR_ERROR(_("Failed to initialize SASL authentication %s"),
- sasl_errstring(err, NULL, NULL));
- goto cleanup;
- }
- }
-#endif
-
-#if HAVE_POLKIT0
- if (auth_unix_rw == REMOTE_AUTH_POLKIT ||
- auth_unix_ro == REMOTE_AUTH_POLKIT) {
- DBusError derr;
-
- dbus_connection_set_change_sigpipe(FALSE);
- dbus_threads_init_default();
-
- dbus_error_init(&derr);
- server->sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr);
- if (!(server->sysbus)) {
- VIR_ERROR(_("Failed to connect to system bus for PolicyKit auth: %s"),
- derr.message);
- dbus_error_free(&derr);
- goto cleanup;
- }
- dbus_connection_set_exit_on_disconnect(server->sysbus, FALSE);
- }
-#endif
-
- if (ipsock) {
- if (listen_tcp && remoteListenTCP (server, listen_addr, tcp_port, QEMUD_SOCK_TYPE_TCP, auth_tcp) < 0)
- goto cleanup;
-
- if (listen_tls) {
- if (remoteInitializeGnuTLS () < 0)
- goto cleanup;
-
- if (remoteListenTCP (server, listen_addr, tls_port, QEMUD_SOCK_TYPE_TLS, auth_tls) < 0)
- goto cleanup;
- }
- }
-
-#ifdef HAVE_AVAHI
- if (server->privileged && mdns_adv) {
- struct libvirtd_mdns_group *group;
- int port = 0;
-
- server->mdns = libvirtd_mdns_new();
-
- if (!mdns_name) {
- char groupname[64], localhost[HOST_NAME_MAX+1], *tmp;
- /* Extract the host part of the potentially FQDN */
- gethostname(localhost, HOST_NAME_MAX);
- localhost[HOST_NAME_MAX] = '\0';
- if ((tmp = strchr(localhost, '.')))
- *tmp = '\0';
- snprintf(groupname, sizeof(groupname)-1, "Virtualization Host %s", localhost);
- groupname[sizeof(groupname)-1] = '\0';
- group = libvirtd_mdns_add_group(server->mdns, groupname);
- } else {
- group = libvirtd_mdns_add_group(server->mdns, mdns_name);
- }
-
- /*
- * See if there's a TLS enabled port we can advertise. Cowardly
- * don't bother to advertise TCP since we don't want people using
- * them for real world apps
- */
- sock = server->sockets;
- while (sock) {
- if (sock->port != -1 && sock->type == QEMUD_SOCK_TYPE_TLS) {
- port = sock->port;
- break;
- }
- sock = sock->next;
- }
-
- /*
- * Add the primary entry - we choose SSH because its most likely to always
- * be available
- */
- libvirtd_mdns_add_entry(group, "_libvirt._tcp", port);
- libvirtd_mdns_start(server->mdns);
- }
-#endif
-
- return server;
-
- cleanup:
- if (server) {
- sock = server->sockets;
- while (sock) {
- close(sock->fd);
- sock = sock->next;
- }
-
-#if HAVE_POLKIT0
- if (server->sysbus)
- dbus_connection_unref(server->sysbus);
-#endif
- free(server);
- }
- return NULL;
-}
-
-static gnutls_session_t
-remoteInitializeTLSSession (void)
-{
- gnutls_session_t session;
- int err;
-
- err = gnutls_init (&session, GNUTLS_SERVER);
- if (err != 0) goto failed;
-
- /* avoid calling all the priority functions, since the defaults
- * are adequate.
- */
- err = gnutls_set_default_priority (session);
- if (err != 0) goto failed;
-
- err = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
- if (err != 0) goto failed;
-
- /* request client certificate if any.
- */
- gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
-
- gnutls_dh_set_prime_bits (session, DH_BITS);
-
- return session;
-
- failed:
- VIR_ERROR(_("remoteInitializeTLSSession: %s"),
- gnutls_strerror (err));
- return NULL;
-}
-
-/* Check DN is on tls_allowed_dn_list. */
-static int
-remoteCheckDN (gnutls_x509_crt_t cert)
-{
- char name[256];
- size_t namesize = sizeof name;
- char **wildcards;
- int err;
-
- err = gnutls_x509_crt_get_dn (cert, name, &namesize);
- if (err != 0) {
- VIR_ERROR(_("remoteCheckDN: gnutls_x509_cert_get_dn: %s"),
- gnutls_strerror (err));
- return 0;
- }
-
- /* If the list is not set, allow any DN. */
- wildcards = tls_allowed_dn_list;
- if (!wildcards)
- return 1;
-
- while (*wildcards) {
- if (fnmatch (*wildcards, name, 0) == 0)
- return 1;
- wildcards++;
- }
-
- /* Print the client's DN. */
- DEBUG(_("remoteCheckDN: failed: client DN is %s"), name);
-
- return 0; // Not found.
-}
-
-static int
-remoteCheckCertificate (gnutls_session_t session)
-{
- int ret;
- unsigned int status;
- const gnutls_datum_t *certs;
- unsigned int nCerts, i;
- time_t now;
-
- if ((ret = gnutls_certificate_verify_peers2 (session, &status)) < 0){
- VIR_ERROR(_("remoteCheckCertificate: verify failed: %s"),
- gnutls_strerror (ret));
- return -1;
- }
-
- if (status != 0) {
- if (status & GNUTLS_CERT_INVALID)
- VIR_ERROR0(_("remoteCheckCertificate: "
- "the client certificate is not trusted."));
-
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- VIR_ERROR0(_("remoteCheckCertificate: the client "
- "certificate has unknown issuer."));
-
- if (status & GNUTLS_CERT_REVOKED)
- VIR_ERROR0(_("remoteCheckCertificate: "
- "the client certificate has been revoked."));
-
-#ifndef GNUTLS_1_0_COMPAT
- if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
- VIR_ERROR0(_("remoteCheckCertificate: the client certificate"
- " uses an insecure algorithm."));
-#endif
-
- return -1;
- }
-
- if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
- VIR_ERROR0(_("remoteCheckCertificate: certificate is not X.509"));
- return -1;
- }
-
- if (!(certs = gnutls_certificate_get_peers(session, &nCerts))) {
- VIR_ERROR0(_("remoteCheckCertificate: no peers"));
- return -1;
- }
-
- now = time (NULL);
-
- for (i = 0; i < nCerts; i++) {
- gnutls_x509_crt_t cert;
-
- if (gnutls_x509_crt_init (&cert) < 0) {
- VIR_ERROR0(_("remoteCheckCertificate: gnutls_x509_crt_init failed"));
- return -1;
- }
-
- if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_expiration_time (cert) < now) {
- VIR_ERROR0(_("remoteCheckCertificate: "
- "the client certificate has expired"));
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VIR_ERROR0(_("remoteCheckCertificate: the client "
- "certificate is not yet activated"));
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (i == 0) {
- if (!remoteCheckDN (cert)) {
- /* This is the most common error: make it informative. */
- VIR_ERROR0(_("remoteCheckCertificate: client's Distinguished Name is not on the list of allowed clients (tls_allowed_dn_list). Use 'openssl x509 -in clientcert.pem -text' to view the Distinguished Name field in the client certificate, or run this daemon with --verbose option."));
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
- }
- }
-
- return 0;
-}
-
-/* Check the client's access. */
-static int
-remoteCheckAccess (struct qemud_client *client)
-{
- struct qemud_client_message *confirm;
-
- /* Verify client certificate. */
- if (remoteCheckCertificate (client->tlssession) == -1) {
- VIR_ERROR0(_("remoteCheckCertificate: "
- "failed to verify client's certificate"));
- if (!tls_no_verify_certificate) return -1;
- else VIR_INFO0(_("remoteCheckCertificate: tls_no_verify_certificate "
- "is set so the bad certificate is ignored"));
- }
-
- if (client->tx) {
- VIR_INFO("%s",
- _("client had unexpected data pending tx after access check"));
- return -1;
- }
-
- if (VIR_ALLOC(confirm) < 0)
- return -1;
-
- /* Checks have succeeded. Write a '\1' byte back to the client to
- * indicate this (otherwise the socket is abruptly closed).
- * (NB. The '\1' byte is sent in an encrypted record).
- */
- confirm->async = 1;
- confirm->bufferLength = 1;
- confirm->bufferOffset = 0;
- confirm->buffer[0] = '\1';
-
- client->tx = confirm;
- return 0;
-}
-
-#if HAVE_POLKIT
-int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid) {
-#ifdef SO_PEERCRED
- struct ucred cr;
- unsigned int cr_len = sizeof (cr);
-
- if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) < 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to verify client credentials: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- return -1;
- }
-
- *pid = cr.pid;
- *uid = cr.uid;
-#else
- /* XXX Many more OS support UNIX socket credentials we could port to. See dbus ....*/
-#error "UNIX socket credentials not supported/implemented on this platform yet..."
-#endif
- return 0;
-}
-#endif
-
-
-static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) {
- int fd;
- struct sockaddr_storage addr;
- socklen_t addrlen = (socklen_t) (sizeof addr);
- struct qemud_client *client;
- int no_slow_start = 1;
-
- if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) {
- char ebuf[1024];
- if (errno == EAGAIN)
- return 0;
- VIR_ERROR(_("Failed to accept connection: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- return -1;
- }
-
- if (server->nclients >= max_clients) {
- VIR_ERROR(_("Too many active clients (%d), dropping connection"), max_clients);
- close(fd);
- return -1;
- }
-
- if (VIR_REALLOC_N(server->clients, server->nclients+1) < 0) {
- VIR_ERROR0(_("Out of memory allocating clients"));
- close(fd);
- return -1;
- }
-
-#ifdef __sun
- {
- ucred_t *ucred = NULL;
- const priv_set_t *privs;
-
- if (getpeerucred (fd, &ucred) == -1 ||
- (privs = ucred_getprivset (ucred, PRIV_EFFECTIVE)) == NULL) {
- if (ucred != NULL)
- ucred_free (ucred);
- close (fd);
- return -1;
- }
-
- if (!priv_ismember (privs, PRIV_VIRT_MANAGE)) {
- ucred_free (ucred);
- close (fd);
- return -1;
- }
-
- ucred_free (ucred);
- }
-#endif /* __sun */
-
- /* Disable Nagle. Unix sockets will ignore this. */
- setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *)&no_slow_start,
- sizeof no_slow_start);
-
- if (virSetCloseExec(fd) < 0 ||
- virSetNonBlock(fd) < 0) {
- close(fd);
- return -1;
- }
-
- if (VIR_ALLOC(client) < 0)
- goto cleanup;
- if (virMutexInit(&client->lock) < 0) {
- VIR_ERROR("%s", _("cannot initialize mutex"));
- VIR_FREE(client);
- goto cleanup;
- }
-
- client->magic = QEMUD_CLIENT_MAGIC;
- client->fd = fd;
- client->readonly = sock->readonly;
- client->type = sock->type;
- client->auth = sock->auth;
- memcpy (&client->addr, &addr, sizeof addr);
- client->addrlen = addrlen;
-
- /* Prepare one for packet receive */
- if (VIR_ALLOC(client->rx) < 0)
- goto cleanup;
- client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
-
-
-#if HAVE_POLKIT
- /* Only do policy checks for non-root - allow root user
- through with no checks, as a fail-safe - root can easily
- change policykit policy anyway, so its pointless trying
- to restrict root */
- if (client->auth == REMOTE_AUTH_POLKIT) {
- uid_t uid;
- pid_t pid;
-
- if (qemudGetSocketIdentity(client->fd, &uid, &pid) < 0)
- goto cleanup;
-
- /* Client is running as root, so disable auth */
- if (uid == 0) {
- VIR_INFO(_("Turn off polkit auth for privileged client %d"), pid);
- client->auth = REMOTE_AUTH_NONE;
- }
- }
-#endif
-
- if (client->type != QEMUD_SOCK_TYPE_TLS) {
- /* Plain socket, so prepare to read first message */
- if (qemudRegisterClientEvent (server, client) < 0)
- goto cleanup;
- } else {
- int ret;
-
- client->tlssession = remoteInitializeTLSSession ();
- if (client->tlssession == NULL)
- goto cleanup;
-
- gnutls_transport_set_ptr (client->tlssession,
- (gnutls_transport_ptr_t) (long) fd);
-
- /* Begin the TLS handshake. */
- ret = gnutls_handshake (client->tlssession);
- if (ret == 0) {
- client->handshake = 0;
-
- /* Unlikely, but ... Next step is to check the certificate. */
- if (remoteCheckAccess (client) == -1)
- goto cleanup;
-
- /* Handshake & cert check OK, so prepare to read first message */
- if (qemudRegisterClientEvent(server, client) < 0)
- goto cleanup;
- } else if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
- /* Most likely, need to do more handshake data */
- client->handshake = 1;
-
- if (qemudRegisterClientEvent (server, client) < 0)
- goto cleanup;
- } else {
- VIR_ERROR(_("TLS handshake failed: %s"),
- gnutls_strerror (ret));
- goto cleanup;
- }
- }
-
- server->clients[server->nclients++] = client;
-
- if (server->nclients > server->nactiveworkers &&
- server->nactiveworkers < server->nworkers) {
- int i;
- for (i = 0 ; i < server->nworkers ; i++) {
- if (!server->workers[i].hasThread) {
- if (qemudStartWorker(server, &server->workers[i]) < 0)
- return -1;
- server->nactiveworkers++;
- break;
- }
- }
- }
-
-
- return 0;
-
- cleanup:
- if (client &&
- client->tlssession) gnutls_deinit (client->tlssession);
- close (fd);
- VIR_FREE(client->rx);
- VIR_FREE(client);
- return -1;
-}
-
-
-/*
- * You must hold lock for at least the client
- * We don't free stuff here, merely disconnect the client's
- * network socket & resources.
- * We keep the libvirt connection open until any async
- * jobs have finished, then clean it up elsehwere
- */
-void qemudDispatchClientFailure(struct qemud_client *client) {
- if (client->watch != -1) {
- virEventRemoveHandleImpl(client->watch);
- client->watch = -1;
- }
-
- /* Deregister event delivery callback */
- if(client->conn) {
- DEBUG0("Deregistering to relay remote events");
- virConnectDomainEventDeregister(client->conn, remoteRelayDomainEvent);
- }
-
-#if HAVE_SASL
- if (client->saslconn) {
- sasl_dispose(&client->saslconn);
- client->saslconn = NULL;
- }
- free(client->saslUsername);
- client->saslUsername = NULL;
-#endif
- if (client->tlssession) {
- gnutls_deinit (client->tlssession);
- client->tlssession = NULL;
- }
- if (client->fd != -1) {
- close(client->fd);
- client->fd = -1;
- }
-}
-
-
-/* Caller must hold server lock */
-static struct qemud_client *qemudPendingJob(struct qemud_server *server)
-{
- int i;
- for (i = 0 ; i < server->nclients ; i++) {
- virMutexLock(&server->clients[i]->lock);
- if (server->clients[i]->dx) {
- /* Delibrately don't unlock client - caller wants the lock */
- return server->clients[i];
- }
- virMutexUnlock(&server->clients[i]->lock);
- }
- return NULL;
-}
-
-static void *qemudWorker(void *data)
-{
- struct qemud_worker *worker = data;
- struct qemud_server *server = worker->server;
-
- while (1) {
- struct qemud_client *client = NULL;
- struct qemud_client_message *msg;
-
- virMutexLock(&server->lock);
- while (((client = qemudPendingJob(server)) == NULL) &&
- !worker->quitRequest) {
- if (virCondWait(&server->job, &server->lock) < 0) {
- virMutexUnlock(&server->lock);
- return NULL;
- }
- }
- if (worker->quitRequest) {
- if (client)
- virMutexUnlock(&client->lock);
- virMutexUnlock(&server->lock);
- return NULL;
- }
- worker->processingCall = 1;
- virMutexUnlock(&server->lock);
-
- /* We own a locked client now... */
- client->refs++;
-
- /* Remove our message from dispatch queue while we use it */
- msg = qemudClientMessageQueueServe(&client->dx);
-
- /* This function drops the lock during dispatch,
- * and re-acquires it before returning */
- if (remoteDispatchClientRequest (server, client, msg) < 0) {
- VIR_FREE(msg);
- qemudDispatchClientFailure(client);
- client->refs--;
- virMutexUnlock(&client->lock);
- continue;
- }
-
- client->refs--;
- virMutexUnlock(&client->lock);
-
- virMutexLock(&server->lock);
- worker->processingCall = 0;
- virMutexUnlock(&server->lock);
- }
-}
-
-static int qemudStartWorker(struct qemud_server *server,
- struct qemud_worker *worker) {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- /* We want to join workers, so don't detach them */
- /*pthread_attr_setdetachstate(&attr, 1);*/
-
- if (worker->hasThread)
- return -1;
-
- worker->server = server;
- worker->hasThread = 1;
- worker->quitRequest = 0;
- worker->processingCall = 0;
-
- if (pthread_create(&worker->thread,
- &attr,
- qemudWorker,
- worker) != 0) {
- worker->hasThread = 0;
- worker->server = NULL;
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- * Read data into buffer using wire decoding (plain or TLS)
- *
- * Returns:
- * -1 on error or EOF
- * 0 on EAGAIN
- * n number of bytes
- */
-static ssize_t qemudClientReadBuf(struct qemud_client *client,
- char *data, ssize_t len) {
- ssize_t ret;
-
- if (len < 0) {
- VIR_ERROR(_("unexpected negative length request %lld"),
- (long long int) len);
- qemudDispatchClientFailure(client);
- return -1;
- }
-
- /*qemudDebug ("qemudClientRead: len = %d", len);*/
-
- if (!client->tlssession) {
- char ebuf[1024];
- ret = read (client->fd, data, len);
- if (ret == -1 && (errno == EAGAIN ||
- errno == EINTR))
- return 0;
- if (ret <= 0) {
- if (ret != 0)
- VIR_ERROR(_("read: %s"),
- virStrerror (errno, ebuf, sizeof ebuf));
- qemudDispatchClientFailure(client);
- return -1;
- }
- } else {
- ret = gnutls_record_recv (client->tlssession, data, len);
-
- if (ret < 0 && (ret == GNUTLS_E_AGAIN ||
- ret == GNUTLS_E_INTERRUPTED))
- return 0;
- if (ret <= 0) {
- if (ret != 0)
- VIR_ERROR(_("gnutls_record_recv: %s"),
- gnutls_strerror (ret));
- qemudDispatchClientFailure(client);
- return -1;
- }
- }
-
- return ret;
-}
-
-/*
- * Read data into buffer without decoding
- *
- * Returns:
- * -1 on error or EOF
- * 0 on EAGAIN
- * n number of bytes
- */
-static ssize_t qemudClientReadPlain(struct qemud_client *client) {
- ssize_t ret;
- ret = qemudClientReadBuf(client,
- client->rx->buffer + client->rx->bufferOffset,
- client->rx->bufferLength - client->rx->bufferOffset);
- if (ret <= 0)
- return ret; /* -1 error, 0 eagain */
-
- client->rx->bufferOffset += ret;
- return ret;
-}
-
-#if HAVE_SASL
-/*
- * Read data into buffer decoding with SASL
- *
- * Returns:
- * -1 on error or EOF
- * 0 on EAGAIN
- * n number of bytes
- */
-static ssize_t qemudClientReadSASL(struct qemud_client *client) {
- ssize_t got, want;
-
- /* We're doing a SSF data read, so now its times to ensure
- * future writes are under SSF too.
- *
- * cf remoteSASLCheckSSF in remote.c
- */
- client->saslSSF |= QEMUD_SASL_SSF_WRITE;
-
- /* Need to read some more data off the wire */
- if (client->saslDecoded == NULL) {
- int ret;
- char encoded[8192];
- ssize_t encodedLen = sizeof(encoded);
- encodedLen = qemudClientReadBuf(client, encoded, encodedLen);
-
- if (encodedLen <= 0)
- return encodedLen;
-
- ret = sasl_decode(client->saslconn, encoded, encodedLen,
- &client->saslDecoded, &client->saslDecodedLength);
- if (ret != SASL_OK) {
- VIR_ERROR(_("failed to decode SASL data %s"),
- sasl_errstring(ret, NULL, NULL));
- qemudDispatchClientFailure(client);
- return -1;
- }
-
- client->saslDecodedOffset = 0;
- }
-
- /* Some buffered decoded data to return now */
- got = client->saslDecodedLength - client->saslDecodedOffset;
- want = client->rx->bufferLength - client->rx->bufferOffset;
-
- if (want > got)
- want = got;
-
- memcpy(client->rx->buffer + client->rx->bufferOffset,
- client->saslDecoded + client->saslDecodedOffset, want);
- client->saslDecodedOffset += want;
- client->rx->bufferOffset += want;
-
- if (client->saslDecodedOffset == client->saslDecodedLength) {
- client->saslDecoded = NULL;
- client->saslDecodedOffset = client->saslDecodedLength = 0;
- }
-
- return want;
-}
-#endif
-
-/*
- * Read as much data off wire as possible till we fill our
- * buffer, or would block on I/O
- */
-static ssize_t qemudClientRead(struct qemud_client *client) {
-#if HAVE_SASL
- if (client->saslSSF & QEMUD_SASL_SSF_READ)
- return qemudClientReadSASL(client);
- else
-#endif
- return qemudClientReadPlain(client);
-}
-
-
-/*
- * Read data until we get a complete message to process
- */
-static void qemudDispatchClientRead(struct qemud_server *server,
- struct qemud_client *client) {
- /*qemudDebug ("qemudDispatchClientRead: mode = %d", client->mode);*/
-
-readmore:
- if (qemudClientRead(client) < 0)
- return; /* Error */
-
- if (client->rx->bufferOffset < client->rx->bufferLength)
- return; /* Still not read enough */
-
- /* Either done with length word header */
- if (client->rx->bufferLength == REMOTE_MESSAGE_HEADER_XDR_LEN) {
- unsigned int len;
- XDR x;
-
- xdrmem_create(&x, client->rx->buffer, client->rx->bufferLength, XDR_DECODE);
-
- if (!xdr_u_int(&x, &len)) {
- xdr_destroy (&x);
- DEBUG0("Failed to decode packet length");
- qemudDispatchClientFailure(client);
- return;
- }
- xdr_destroy (&x);
-
- if (len < REMOTE_MESSAGE_HEADER_XDR_LEN) {
- DEBUG("Packet length %u too small", len);
- qemudDispatchClientFailure(client);
- return;
- }
-
- /* Length includes the size of the length word itself */
- len -= REMOTE_MESSAGE_HEADER_XDR_LEN;
-
- if (len > REMOTE_MESSAGE_MAX) {
- DEBUG("Packet length %u too large", len);
- qemudDispatchClientFailure(client);
- return;
- }
-
- /* Prepare to read rest of message */
- client->rx->bufferLength += len;
-
- qemudUpdateClientEvent(client);
-
- /* Try and read payload immediately instead of going back
- into poll() because chances are the data is already
- waiting for us */
- goto readmore;
- } else {
- /* Grab the completed message */
- struct qemud_client_message *msg = qemudClientMessageQueueServe(&client->rx);
- struct qemud_client_filter *filter;
-
- /* Decode the header so we can use it for routing decisions */
- if (remoteDecodeClientMessageHeader(msg) < 0) {
- VIR_FREE(msg);
- qemudDispatchClientFailure(client);
- }
-
- /* Check if any filters match this message */
- filter = client->filters;
- while (filter) {
- if ((filter->query)(msg, filter->opaque)) {
- qemudClientMessageQueuePush(&filter->dx, msg);
- msg = NULL;
- break;
- }
- filter = filter->next;
- }
-
- /* Move completed message to the end of the dispatch queue */
- if (msg)
- qemudClientMessageQueuePush(&client->dx, msg);
- client->nrequests++;
-
- /* Possibly need to create another receive buffer */
- if ((client->nrequests < max_client_requests &&
- VIR_ALLOC(client->rx) < 0)) {
- qemudDispatchClientFailure(client);
- } else {
- if (client->rx)
- client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
-
- qemudUpdateClientEvent(client);
-
- /* Tell one of the workers to get on with it... */
- virCondSignal(&server->job);
- }
- }
-}
-
-
-/*
- * Send a chunk of data using wire encoding (plain or TLS)
- *
- * Returns:
- * -1 on error
- * 0 on EAGAIN
- * n number of bytes
- */
-static ssize_t qemudClientWriteBuf(struct qemud_client *client,
- const char *data, ssize_t len) {
- ssize_t ret;
-
- if (len < 0) {
- VIR_ERROR(_("unexpected negative length request %lld"),
- (long long int) len);
- qemudDispatchClientFailure(client);
- return -1;
- }
-
- if (!client->tlssession) {
- char ebuf[1024];
- if ((ret = write(client->fd, data, len)) == -1) {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
- VIR_ERROR(_("write: %s"), virStrerror (errno, ebuf, sizeof ebuf));
- qemudDispatchClientFailure(client);
- return -1;
- }
- } else {
- ret = gnutls_record_send (client->tlssession, data, len);
- if (ret < 0) {
- if (ret == GNUTLS_E_INTERRUPTED ||
- ret == GNUTLS_E_AGAIN)
- return 0;
-
- VIR_ERROR(_("gnutls_record_send: %s"), gnutls_strerror (ret));
- qemudDispatchClientFailure(client);
- return -1;
- }
- }
- return ret;
-}
-
-
-/*
- * Send client->tx using no encoding
- *
- * Returns:
- * -1 on error or EOF
- * 0 on EAGAIN
- * n number of bytes
- */
-static int qemudClientWritePlain(struct qemud_client *client) {
- int ret = qemudClientWriteBuf(client,
- client->tx->buffer + client->tx->bufferOffset,
- client->tx->bufferLength - client->tx->bufferOffset);
- if (ret <= 0)
- return ret; /* -1 error, 0 = egain */
- client->tx->bufferOffset += ret;
- return ret;
-}
-
-
-#if HAVE_SASL
-/*
- * Send client->tx using SASL encoding
- *
- * Returns:
- * -1 on error
- * 0 on EAGAIN
- * n number of bytes
- */
-static int qemudClientWriteSASL(struct qemud_client *client) {
- int ret;
-
- /* Not got any pending encoded data, so we need to encode raw stuff */
- if (client->saslEncoded == NULL) {
- ret = sasl_encode(client->saslconn,
- client->tx->buffer + client->tx->bufferOffset,
- client->tx->bufferLength - client->tx->bufferOffset,
- &client->saslEncoded,
- &client->saslEncodedLength);
-
- if (ret != SASL_OK) {
- VIR_ERROR(_("failed to encode SASL data %s"),
- sasl_errstring(ret, NULL, NULL));
- qemudDispatchClientFailure(client);
- return -1;
- }
-
- client->saslEncodedOffset = 0;
- }
-
- /* Send some of the encoded stuff out on the wire */
- ret = qemudClientWriteBuf(client,
- client->saslEncoded + client->saslEncodedOffset,
- client->saslEncodedLength - client->saslEncodedOffset);
-
- if (ret <= 0)
- return ret; /* -1 error, 0 == egain */
-
- /* Note how much we sent */
- client->saslEncodedOffset += ret;
-
- /* Sent all encoded, so update raw buffer to indicate completion */
- if (client->saslEncodedOffset == client->saslEncodedLength) {
- client->saslEncoded = NULL;
- client->saslEncodedOffset = client->saslEncodedLength = 0;
-
- /* Mark as complete, so caller detects completion */
- client->tx->bufferOffset = client->tx->bufferLength;
- }
-
- return ret;
-}
-#endif
-
-/*
- * Send as much data in the client->tx as possible
- *
- * Returns:
- * -1 on error or EOF
- * 0 on EAGAIN
- * n number of bytes
- */
-static ssize_t qemudClientWrite(struct qemud_client *client) {
-#if HAVE_SASL
- if (client->saslSSF & QEMUD_SASL_SSF_WRITE)
- return qemudClientWriteSASL(client);
- else
-#endif
- return qemudClientWritePlain(client);
-}
-
-
-/*
- * Process all queued client->tx messages until
- * we would block on I/O
- */
-static void
-qemudDispatchClientWrite(struct qemud_client *client) {
- while (client->tx) {
- ssize_t ret;
-
- ret = qemudClientWrite(client);
- if (ret < 0) {
- qemudDispatchClientFailure(client);
- return;
- }
- if (ret == 0)
- return; /* Would block on write EAGAIN */
-
- if (client->tx->bufferOffset == client->tx->bufferLength) {
- struct qemud_client_message *reply;
-
- /* Get finished reply from head of tx queue */
- reply = qemudClientMessageQueueServe(&client->tx);
-
- /* If its not an async message, then we have
- * just completed an RPC request */
- if (!reply->async)
- client->nrequests--;
-
- /* Move record to end of 'rx' ist */
- if (!client->rx &&
- client->nrequests < max_client_requests) {
- /* Reset message record for next RX attempt */
- client->rx = reply;
- client->rx->bufferOffset = 0;
- client->rx->bufferLength = REMOTE_MESSAGE_HEADER_XDR_LEN;
- } else {
- VIR_FREE(reply);
- }
-
- if (client->closing)
- qemudDispatchClientFailure(client);
- else
- qemudUpdateClientEvent(client);
- }
- }
-}
-
-static void
-qemudDispatchClientHandshake(struct qemud_client *client) {
- int ret;
- /* Continue the handshake. */
- ret = gnutls_handshake (client->tlssession);
- if (ret == 0) {
- client->handshake = 0;
-
- /* Finished. Next step is to check the certificate. */
- if (remoteCheckAccess (client) == -1)
- qemudDispatchClientFailure(client);
- else
- qemudUpdateClientEvent(client);
- } else if (ret == GNUTLS_E_AGAIN ||
- ret == GNUTLS_E_INTERRUPTED) {
- /* Carry on waiting for more handshake. Update
- the events just in case handshake data flow
- direction has changed */
- qemudUpdateClientEvent (client);
- } else {
- /* Fatal error in handshake */
- VIR_ERROR(_("TLS handshake failed: %s"),
- gnutls_strerror (ret));
- qemudDispatchClientFailure(client);
- }
-}
-
-static void
-qemudDispatchClientEvent(int watch, int fd, int events, void *opaque) {
- struct qemud_server *server = (struct qemud_server *)opaque;
- struct qemud_client *client = NULL;
- int i;
-
- virMutexLock(&server->lock);
-
- for (i = 0 ; i < server->nclients ; i++) {
- virMutexLock(&server->clients[i]->lock);
- if (server->clients[i]->watch == watch) {
- client = server->clients[i];
- break;
- }
- virMutexUnlock(&server->clients[i]->lock);
- }
-
- virMutexUnlock(&server->lock);
-
- if (!client) {
- return;
- }
-
- if (client->fd != fd) {
- virMutexUnlock(&client->lock);
- return;
- }
-
- if (events & (VIR_EVENT_HANDLE_WRITABLE |
- VIR_EVENT_HANDLE_READABLE)) {
- if (client->handshake) {
- qemudDispatchClientHandshake(client);
- } else {
- if (events & VIR_EVENT_HANDLE_WRITABLE)
- qemudDispatchClientWrite(client);
- if (events & VIR_EVENT_HANDLE_READABLE)
- qemudDispatchClientRead(server, client);
- }
- }
-
- /* NB, will get HANGUP + READABLE at same time upon
- * disconnect */
- if (events & (VIR_EVENT_HANDLE_ERROR |
- VIR_EVENT_HANDLE_HANGUP))
- qemudDispatchClientFailure(client);
-
- virMutexUnlock(&client->lock);
-}
-
-
-/*
- * @client: a locked client object
- */
-static int
-qemudCalculateHandleMode(struct qemud_client *client) {
- int mode = 0;
-
- if (client->handshake) {
- if (gnutls_record_get_direction (client->tlssession) == 0)
- mode |= VIR_EVENT_HANDLE_READABLE;
- else
- mode |= VIR_EVENT_HANDLE_WRITABLE;
- } else {
- /* If there is a message on the rx queue then
- * we're wanting more input */
- if (client->rx)
- mode |= VIR_EVENT_HANDLE_READABLE;
-
- /* If there are one or more messages to send back to client,
- then monitor for writability on socket */
- if (client->tx)
- mode |= VIR_EVENT_HANDLE_WRITABLE;
- }
-
- return mode;
-}
-
-/*
- * @server: a locked or unlocked server object
- * @client: a locked client object
- */
-int qemudRegisterClientEvent(struct qemud_server *server,
- struct qemud_client *client) {
- int mode;
-
- mode = qemudCalculateHandleMode(client);
-
- if ((client->watch = virEventAddHandleImpl(client->fd,
- mode,
- qemudDispatchClientEvent,
- server, NULL)) < 0)
- return -1;
-
- return 0;
-}
-
-/*
- * @client: a locked client object
- */
-void qemudUpdateClientEvent(struct qemud_client *client) {
- int mode;
-
- mode = qemudCalculateHandleMode(client);
-
- virEventUpdateHandleImpl(client->watch, mode);
-}
-
-
-static void
-qemudDispatchServerEvent(int watch, int fd, int events, void *opaque) {
- struct qemud_server *server = (struct qemud_server *)opaque;
- struct qemud_socket *sock;
-
- virMutexLock(&server->lock);
-
- sock = server->sockets;
-
- while (sock) {
- if (sock->watch == watch)
- break;
-
- sock = sock->next;
- }
-
- if (sock && sock->fd == fd && events)
- qemudDispatchServer(server, sock);
-
- virMutexUnlock(&server->lock);
-}
-
-
-static int qemudOneLoop(void) {
- sig_atomic_t errors;
-
- if (virEventRunOnce() < 0)
- return -1;
-
- /* Check for any signal handling errors and log them. */
- errors = sig_errors;
- if (errors) {
- char ebuf[1024];
- sig_errors -= errors;
- VIR_ERROR(_("Signal handler reported %d errors: last error: %s"),
- errors, virStrerror (sig_lasterrno, ebuf, sizeof ebuf));
- return -1;
- }
-
- return 0;
-}
-
-static void qemudInactiveTimer(int timerid, void *data) {
- struct qemud_server *server = (struct qemud_server *)data;
-
- if (virStateActive() ||
- server->clients) {
- DEBUG0("Timer expired but still active, not shutting down");
- virEventUpdateTimeoutImpl(timerid, -1);
- } else {
- DEBUG0("Timer expired and inactive, shutting down");
- server->shutdown = 1;
- }
-}
-
-static void qemudFreeClient(struct qemud_client *client) {
- while (client->rx) {
- struct qemud_client_message *msg
- = qemudClientMessageQueueServe(&client->rx);
- VIR_FREE(msg);
- }
- while (client->dx) {
- struct qemud_client_message *msg
- = qemudClientMessageQueueServe(&client->dx);
- VIR_FREE(msg);
- }
- while (client->tx) {
- struct qemud_client_message *msg
- = qemudClientMessageQueueServe(&client->tx);
- VIR_FREE(msg);
- }
-
- if (client->conn)
- virConnectClose(client->conn);
- virMutexDestroy(&client->lock);
- VIR_FREE(client);
-}
-
-static int qemudRunLoop(struct qemud_server *server) {
- int timerid = -1;
- int ret = -1, i;
- int timerActive = 0;
-
- virMutexLock(&server->lock);
-
- if (timeout > 0 &&
- (timerid = virEventAddTimeoutImpl(-1,
- qemudInactiveTimer,
- server, NULL)) < 0) {
- VIR_ERROR0(_("Failed to register shutdown timeout"));
- return -1;
- }
-
- if (min_workers > max_workers)
- max_workers = min_workers;
-
- server->nworkers = max_workers;
- if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) {
- VIR_ERROR0(_("Failed to allocate workers"));
- return -1;
- }
-
- for (i = 0 ; i < min_workers ; i++) {
- if (qemudStartWorker(server, &server->workers[i]) < 0)
- goto cleanup;
- server->nactiveworkers++;
- }
-
- for (;;) {
- /* A shutdown timeout is specified, so check
- * if any drivers have active state, if not
- * shutdown after timeout seconds
- */
- if (timeout > 0) {
- if (timerActive) {
- if (server->clients) {
- DEBUG("Deactivating shutdown timer %d", timerid);
- virEventUpdateTimeoutImpl(timerid, -1);
- timerActive = 0;
- }
- } else {
- if (!virStateActive() &&
- !server->clients) {
- DEBUG("Activating shutdown timer %d", timerid);
- virEventUpdateTimeoutImpl(timerid, timeout * 1000);
- timerActive = 1;
- }
- }
- }
-
- virMutexUnlock(&server->lock);
- if (qemudOneLoop() < 0) {
- virMutexLock(&server->lock);
- DEBUG0("Loop iteration error, exiting\n");
- break;
- }
- virMutexLock(&server->lock);
-
- reprocess:
- for (i = 0 ; i < server->nclients ; i++) {
- int inactive;
- virMutexLock(&server->clients[i]->lock);
- inactive = server->clients[i]->fd == -1
- && server->clients[i]->refs == 0;
- virMutexUnlock(&server->clients[i]->lock);
- if (inactive) {
- qemudFreeClient(server->clients[i]);
- server->nclients--;
- if (i < server->nclients)
- memmove(server->clients + i,
- server->clients + i + 1,
- sizeof (*server->clients) * (server->nclients - i));
-
- if (VIR_REALLOC_N(server->clients,
- server->nclients) < 0) {
- ; /* ignore */
- }
- goto reprocess;
- }
- }
-
- /* If number of active workers exceeds both the min_workers
- * threshold and the number of clients, then kill some
- * off */
- for (i = 0 ; (i < server->nworkers &&
- server->nactiveworkers > server->nclients &&
- server->nactiveworkers > min_workers) ; i++) {
-
- if (server->workers[i].hasThread &&
- !server->workers[i].processingCall) {
- server->workers[i].quitRequest = 1;
-
- virCondBroadcast(&server->job);
- virMutexUnlock(&server->lock);
- pthread_join(server->workers[i].thread, NULL);
- virMutexLock(&server->lock);
- server->workers[i].hasThread = 0;
- server->nactiveworkers--;
- }
- }
-
- if (server->shutdown) {
- ret = 0;
- break;
- }
- }
-
-cleanup:
- for (i = 0 ; i < server->nworkers ; i++) {
- if (!server->workers[i].hasThread)
- continue;
-
- server->workers[i].quitRequest = 1;
- virCondBroadcast(&server->job);
-
- virMutexUnlock(&server->lock);
- pthread_join(server->workers[i].thread, NULL);
- virMutexLock(&server->lock);
- server->workers[i].hasThread = 0;
- }
- VIR_FREE(server->workers);
-
- free(server->workers);
- virMutexUnlock(&server->lock);
- return ret;
-}
-
-static void qemudCleanup(struct qemud_server *server) {
- struct qemud_socket *sock;
-
- close(server->sigread);
-
- sock = server->sockets;
- while (sock) {
- struct qemud_socket *next = sock->next;
- close(sock->fd);
- free(sock);
- sock = next;
- }
- free(server->logDir);
-
-#ifdef HAVE_SASL
- if (server->saslUsernameWhitelist) {
- char **list = server->saslUsernameWhitelist;
- while (*list) {
- free(*list);
- list++;
- }
- free(server->saslUsernameWhitelist);
- }
-#endif
-
- virStateCleanup();
-
- if (virCondDestroy(&server->job) < 0) {
- ;
- }
- virMutexDestroy(&server->lock);
-
- VIR_FREE(server);
-}
-
-/* Allocate an array of malloc'd strings from the config file, filename
- * (used only in diagnostics), using handle "conf". Upon error, return -1
- * and free any allocated memory. Otherwise, save the array in *list_arg
- * and return 0.
- */
-static int
-remoteConfigGetStringList(virConfPtr conf, const char *key, char ***list_arg,
- const char *filename)
-{
- char **list;
- virConfValuePtr p = virConfGetValue (conf, key);
- if (!p)
- return 0;
-
- switch (p->type) {
- case VIR_CONF_STRING:
- if (VIR_ALLOC_N(list, 2) < 0) {
- VIR_ERROR(_("failed to allocate memory for %s config list"), key);
- return -1;
- }
- list[0] = strdup (p->str);
- list[1] = NULL;
- if (list[0] == NULL) {
- VIR_ERROR(_("failed to allocate memory for %s config list value"),
- key);
- VIR_FREE(list);
- return -1;
- }
- break;
-
- case VIR_CONF_LIST: {
- int i, len = 0;
- virConfValuePtr pp;
- for (pp = p->list; pp; pp = pp->next)
- len++;
- if (VIR_ALLOC_N(list, 1+len) < 0) {
- VIR_ERROR(_("failed to allocate memory for %s config list"), key);
- return -1;
- }
- for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
- if (pp->type != VIR_CONF_STRING) {
- VIR_ERROR(_("remoteReadConfigFile: %s: %s:"
- " must be a string or list of strings\n"),
- filename, key);
- VIR_FREE(list);
- return -1;
- }
- list[i] = strdup (pp->str);
- if (list[i] == NULL) {
- int j;
- for (j = 0 ; j < i ; j++)
- VIR_FREE(list[j]);
- VIR_FREE(list);
- VIR_ERROR(_("failed to allocate memory for %s config list value"),
- key);
- return -1;
- }
-
- }
- list[i] = NULL;
- break;
- }
-
- default:
- VIR_ERROR(_("remoteReadConfigFile: %s: %s:"
- " must be a string or list of strings\n"),
- filename, key);
- return -1;
- }
-
- *list_arg = list;
- return 0;
-}
-
-/* A helper function used by each of the following macros. */
-static int
-checkType (virConfValuePtr p, const char *filename,
- const char *key, virConfType required_type)
-{
- if (p->type != required_type) {
- VIR_ERROR(_("remoteReadConfigFile: %s: %s: invalid type:"
- " got %s; expected %s\n"), filename, key,
- virConfTypeName (p->type),
- virConfTypeName (required_type));
- return -1;
- }
- return 0;
-}
-
-/* If there is no config data for the key, #var_name, then do nothing.
- If there is valid data of type VIR_CONF_STRING, and strdup succeeds,
- store the result in var_name. Otherwise, (i.e. invalid type, or strdup
- failure), give a diagnostic and "goto" the cleanup-and-fail label. */
-#define GET_CONF_STR(conf, filename, var_name) \
- do { \
- virConfValuePtr p = virConfGetValue (conf, #var_name); \
- if (p) { \
- if (checkType (p, filename, #var_name, VIR_CONF_STRING) < 0) \
- goto free_and_fail; \
- (var_name) = strdup (p->str); \
- if ((var_name) == NULL) { \
- char ebuf[1024]; \
- VIR_ERROR(_("remoteReadConfigFile: %s\n"), \
- virStrerror(errno, ebuf, sizeof ebuf)); \
- goto free_and_fail; \
- } \
- } \
- } while (0)
-
-/* Like GET_CONF_STR, but for integral values. */
-#define GET_CONF_INT(conf, filename, var_name) \
- do { \
- virConfValuePtr p = virConfGetValue (conf, #var_name); \
- if (p) { \
- if (checkType (p, filename, #var_name, VIR_CONF_LONG) < 0) \
- goto free_and_fail; \
- (var_name) = p->l; \
- } \
- } while (0)
-
-
-static int remoteConfigGetAuth(virConfPtr conf, const char *key, int *auth, const char *filename) {
- virConfValuePtr p;
-
- p = virConfGetValue (conf, key);
- if (!p)
- return 0;
-
- if (checkType (p, filename, key, VIR_CONF_STRING) < 0)
- return -1;
-
- if (!p->str)
- return 0;
-
- if (STREQ(p->str, "none")) {
- *auth = REMOTE_AUTH_NONE;
-#if HAVE_SASL
- } else if (STREQ(p->str, "sasl")) {
- *auth = REMOTE_AUTH_SASL;
-#endif
-#if HAVE_POLKIT
- } else if (STREQ(p->str, "polkit")) {
- *auth = REMOTE_AUTH_POLKIT;
-#endif
- } else {
- VIR_ERROR(_("remoteReadConfigFile: %s: %s: unsupported auth %s\n"),
- filename, key, p->str);
- return -1;
- }
-
- return 0;
-}
-
-#ifdef HAVE_SASL
-static inline int
-remoteReadSaslAllowedUsernameList (virConfPtr conf,
- struct qemud_server *server,
- const char *filename)
-{
- return
- remoteConfigGetStringList (conf, "sasl_allowed_username_list",
- &server->saslUsernameWhitelist, filename);
-}
-#else
-static inline int
-remoteReadSaslAllowedUsernameList (virConfPtr conf ATTRIBUTE_UNUSED,
- struct qemud_server *server ATTRIBUTE_UNUSED,
- const char *filename ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-#endif
-
-/*
- * Set up the logging environment
- * By default if daemonized all errors go to syslog and the logging
- * is also saved onto the logfile libvird.log, but if verbose or error
- * debugging is asked for then output informations or debug.
- */
-static int
-qemudSetLogging(virConfPtr conf, const char *filename) {
- int log_level;
- char *log_filters = NULL;
- char *log_outputs = NULL;
- int ret = -1;
-
- virLogReset();
-
- /*
- * Libvirtd's order of precedence is:
- * cmdline > environment > config
- *
- * In order to achieve this, we must process configuration in
- * different order for the log level versus the filters and
- * outputs. Because filters and outputs append, we have to look at
- * the environment first and then only check the config file if
- * there was no result from the environment. The default output is
- * then applied only if there was no setting from either of the
- * first two. Because we don't have a way to determine if the log
- * level has been set, we must process variables in the opposite
- * order, each one overriding the previous.
- */
- /*
- * GET_CONF_INT returns 0 when there is no log_level setting in
- * the config file. The conditional below eliminates a false
- * warning in that case, but also has the side effect of missing
- * a warning if the user actually does say log_level=0.
- */
- GET_CONF_INT (conf, filename, log_level);
- if (log_level != 0)
- virLogSetDefaultPriority(log_level);
-
- virLogSetFromEnv();
-
- if (virLogGetNbFilters() == 0) {
- GET_CONF_STR (conf, filename, log_filters);
- virLogParseFilters(log_filters);
- }
-
- if (virLogGetNbOutputs() == 0) {
- GET_CONF_STR (conf, filename, log_outputs);
- virLogParseOutputs(log_outputs);
- }
-
- /*
- * If no defined outputs, then direct to syslog when running
- * as daemon. Otherwise the default output is stderr.
- */
- if (virLogGetNbOutputs() == 0) {
- char *tmp = NULL;
- if (godaemon) {
- if (virAsprintf (&tmp, "%d:syslog:libvirtd",
- virLogGetDefaultPriority()) < 0)
- goto free_and_fail;
- } else {
- if (virAsprintf (&tmp, "%d:stderr",
- virLogGetDefaultPriority()) < 0)
- goto free_and_fail;
- }
- virLogParseOutputs(tmp);
- VIR_FREE(tmp);
- }
-
- /*
- * Command line override for --verbose
- */
- if ((verbose) && (virLogGetDefaultPriority() > VIR_LOG_INFO))
- virLogSetDefaultPriority(VIR_LOG_INFO);
-
- ret = 0;
-
-free_and_fail:
- VIR_FREE(log_filters);
- VIR_FREE(log_outputs);
- return(ret);
-}
-
-/* Read the config file if it exists.
- * Only used in the remote case, hence the name.
- */
-static int
-remoteReadConfigFile (struct qemud_server *server, const char *filename)
-{
- virConfPtr conf;
-
- /* The following variable names must match the corresponding
- configuration strings. */
- char *unix_sock_ro_perms = NULL;
- char *unix_sock_rw_perms = NULL;
- char *unix_sock_group = NULL;
- char *buf = NULL;
-
-#if HAVE_POLKIT
- /* Change the default back to no auth for non-root */
- if (!server->privileged && auth_unix_rw == REMOTE_AUTH_POLKIT)
- auth_unix_rw = REMOTE_AUTH_NONE;
- if (!server->privileged && auth_unix_ro == REMOTE_AUTH_POLKIT)
- auth_unix_ro = REMOTE_AUTH_NONE;
-#endif
-
- conf = virConfReadFile (filename, 0);
- if (!conf) return -1;
-
- /*
- * First get all the logging settings and activate them
- */
- if (qemudSetLogging(conf, filename) < 0)
- goto free_and_fail;
-
- GET_CONF_INT (conf, filename, listen_tcp);
- GET_CONF_INT (conf, filename, listen_tls);
- GET_CONF_STR (conf, filename, tls_port);
- GET_CONF_STR (conf, filename, tcp_port);
- GET_CONF_STR (conf, filename, listen_addr);
-
- if (remoteConfigGetAuth(conf, "auth_unix_rw", &auth_unix_rw, filename) < 0)
- goto free_and_fail;
-#if HAVE_POLKIT
- /* Change default perms to be wide-open if PolicyKit is enabled.
- * Admin can always override in config file
- */
- if (auth_unix_rw == REMOTE_AUTH_POLKIT)
- unix_sock_rw_mask = 0777;
-#endif
- if (remoteConfigGetAuth(conf, "auth_unix_ro", &auth_unix_ro, filename) < 0)
- goto free_and_fail;
- if (remoteConfigGetAuth(conf, "auth_tcp", &auth_tcp, filename) < 0)
- goto free_and_fail;
- if (remoteConfigGetAuth(conf, "auth_tls", &auth_tls, filename) < 0)
- goto free_and_fail;
-
- GET_CONF_STR (conf, filename, unix_sock_group);
- if (unix_sock_group) {
- if (!server->privileged) {
- VIR_WARN0(_("Cannot set group when not running as root"));
- } else {
- int ret;
- struct group grpdata, *grp;
- size_t maxbuf = sysconf(_SC_GETGR_R_SIZE_MAX);
-
- if (maxbuf == -1)
- maxbuf = 1024;
-
- if (VIR_ALLOC_N(buf, maxbuf) < 0) {
- VIR_ERROR("%s", _("Failed to allocate memory for buffer"));
- goto free_and_fail;
- }
-
- while ((ret = getgrnam_r(unix_sock_group, &grpdata,
- buf, maxbuf,
- &grp)) == ERANGE) {
- maxbuf *= 2;
- if (maxbuf > 65536 || VIR_REALLOC_N(buf, maxbuf) < 0) {
- VIR_ERROR("%s", _("Failed to reallocate enough memory for buffer"));
- goto free_and_fail;
- }
- }
-
- if (ret != 0 || !grp) {
- VIR_ERROR(_("Failed to lookup group '%s'"), unix_sock_group);
- goto free_and_fail;
- }
- unix_sock_gid = grp->gr_gid;
- VIR_FREE (buf);
- }
- free (unix_sock_group);
- unix_sock_group = NULL;
- }
-
- GET_CONF_STR (conf, filename, unix_sock_ro_perms);
- if (unix_sock_ro_perms) {
- if (virStrToLong_i (unix_sock_ro_perms, NULL, 8, &unix_sock_ro_mask) != 0) {
- VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_ro_perms);
- goto free_and_fail;
- }
- free (unix_sock_ro_perms);
- unix_sock_ro_perms = NULL;
- }
-
- GET_CONF_STR (conf, filename, unix_sock_rw_perms);
- if (unix_sock_rw_perms) {
- if (virStrToLong_i (unix_sock_rw_perms, NULL, 8, &unix_sock_rw_mask) != 0) {
- VIR_ERROR(_("Failed to parse mode '%s'"), unix_sock_rw_perms);
- goto free_and_fail;
- }
- free (unix_sock_rw_perms);
- unix_sock_rw_perms = NULL;
- }
-
- GET_CONF_STR (conf, filename, unix_sock_dir);
-
- GET_CONF_INT (conf, filename, mdns_adv);
- GET_CONF_STR (conf, filename, mdns_name);
-
- GET_CONF_INT (conf, filename, tls_no_verify_certificate);
-
- GET_CONF_STR (conf, filename, key_file);
- GET_CONF_STR (conf, filename, cert_file);
- GET_CONF_STR (conf, filename, ca_file);
- GET_CONF_STR (conf, filename, crl_file);
-
- if (remoteConfigGetStringList (conf, "tls_allowed_dn_list",
- &tls_allowed_dn_list, filename) < 0)
- goto free_and_fail;
-
- if (remoteReadSaslAllowedUsernameList (conf, server, filename) < 0)
- goto free_and_fail;
-
-
- GET_CONF_INT (conf, filename, min_workers);
- GET_CONF_INT (conf, filename, max_workers);
- GET_CONF_INT (conf, filename, max_clients);
-
- GET_CONF_INT (conf, filename, max_requests);
- GET_CONF_INT (conf, filename, max_client_requests);
-
- virConfFree (conf);
- return 0;
-
- free_and_fail:
- virConfFree (conf);
- free (mdns_name);
- mdns_name = NULL;
- free (unix_sock_ro_perms);
- free (unix_sock_rw_perms);
- free (unix_sock_group);
- VIR_FREE (buf);
-
- /* Don't bother trying to free listen_addr, tcp_port, tls_port, key_file,
- cert_file, ca_file, or crl_file, since they are initialized to
- non-malloc'd strings. Besides, these are static variables, and callers
- are unlikely to call this function more than once, so there wouldn't
- even be a real leak. */
-
- if (tls_allowed_dn_list) {
- int i;
- for (i = 0; tls_allowed_dn_list[i]; i++)
- free (tls_allowed_dn_list[i]);
- free (tls_allowed_dn_list);
- tls_allowed_dn_list = NULL;
- }
-
- return -1;
-}
-
-/* Display version information. */
-static void
-version (const char *argv0)
-{
- printf ("%s (%s) %s\n", argv0, PACKAGE_NAME, PACKAGE_VERSION);
-}
-
-#ifdef __sun
-static int
-qemudSetupPrivs (void)
-{
- chown ("/var/run/libvirt", SYSTEM_UID, SYSTEM_UID);
-
- if (__init_daemon_priv (PU_RESETGROUPS | PU_CLEARLIMITSET,
- SYSTEM_UID, SYSTEM_UID, PRIV_XVM_CONTROL, NULL)) {
- VIR_ERROR0(_("additional privileges are required\n"));
- return -1;
- }
-
- if (priv_set (PRIV_OFF, PRIV_ALLSETS, PRIV_FILE_LINK_ANY, PRIV_PROC_INFO,
- PRIV_PROC_SESSION, PRIV_PROC_EXEC, PRIV_PROC_FORK, NULL)) {
- VIR_ERROR0(_("failed to set reduced privileges\n"));
- return -1;
- }
-
- return 0;
-}
-#else
-#define qemudSetupPrivs() 0
-#endif
-
-/* Print command-line usage. */
-static void
-usage (const char *argv0)
-{
- fprintf (stderr,
- "\n\
-Usage:\n\
- %s [options]\n\
-\n\
-Options:\n\
- -v | --verbose Verbose messages.\n\
- -d | --daemon Run as a daemon & write PID file.\n\
- -l | --listen Listen for TCP/IP connections.\n\
- -t | --timeout <secs> Exit after timeout period.\n\
- -f | --config <file> Configuration file.\n\
- | --version Display version information.\n\
- -p | --pid-file <file> Change name of PID file.\n\
-\n\
-libvirt management daemon:\n\
-\n\
- Default paths:\n\
-\n\
- Configuration file (unless overridden by -c):\n\
- " SYSCONF_DIR "/libvirt/libvirtd.conf\n\
-\n\
- Sockets (as root):\n\
- " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock\n\
- " LOCAL_STATE_DIR "/run/libvirt/libvirt-sock-ro\n\
-\n\
- Sockets (as non-root):\n\
- $HOME/.libvirt/libvirt-sock (in UNIX abstract namespace)\n\
-\n\
- TLS:\n\
- CA certificate: " LIBVIRT_CACERT "\n\
- Server certificate: " LIBVIRT_SERVERCERT "\n\
- Server private key: " LIBVIRT_SERVERKEY "\n\
-\n\
- PID file (unless overridden by --pid-file):\n\
- %s\n\
-\n",
- argv0,
- REMOTE_PID_FILE[0] != '\0'
- ? REMOTE_PID_FILE
- : "(disabled in ./configure)");
-}
-
-enum {
- OPT_VERSION = 129
-};
-
-#define MAX_LISTEN 5
-int main(int argc, char **argv) {
- struct qemud_server *server = NULL;
- struct sigaction sig_action;
- int sigpipe[2];
- const char *pid_file = NULL;
- const char *remote_config_file = NULL;
- int ret = 1;
-
- struct option opts[] = {
- { "verbose", no_argument, &verbose, 1},
- { "daemon", no_argument, &godaemon, 1},
- { "listen", no_argument, &ipsock, 1},
- { "config", required_argument, NULL, 'f'},
- { "timeout", required_argument, NULL, 't'},
- { "pid-file", required_argument, NULL, 'p'},
- { "version", no_argument, NULL, OPT_VERSION },
- { "help", no_argument, NULL, '?' },
- {0, 0, 0, 0}
- };
-
- while (1) {
- int optidx = 0;
- int c;
- char *tmp;
-
- c = getopt_long(argc, argv, "ldf:p:t:v", opts, &optidx);
-
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 0:
- /* Got one of the flags */
- break;
- case 'v':
- verbose = 1;
- break;
- case 'd':
- godaemon = 1;
- break;
- case 'l':
- ipsock = 1;
- break;
-
- case 't':
- if (virStrToLong_i(optarg, &tmp, 10, &timeout) != 0
- || timeout <= 0
- /* Ensure that we can multiply by 1000 without overflowing. */
- || timeout > INT_MAX / 1000)
- timeout = -1;
- break;
-
- case 'p':
- pid_file = optarg;
- break;
-
- case 'f':
- remote_config_file = optarg;
- break;
-
- case OPT_VERSION:
- version (argv[0]);
- return 0;
-
- case '?':
- usage (argv[0]);
- return 2;
-
- default:
- fprintf (stderr, "libvirtd: internal error: unknown flag: %c\n",
- c);
- exit (1);
- }
- }
-
- if (remote_config_file == NULL) {
- static const char *default_config_file
- = SYSCONF_DIR "/libvirt/libvirtd.conf";
- remote_config_file =
- (access(default_config_file, R_OK) == 0
- ? default_config_file
- : "/dev/null");
- }
-
- if (godaemon) {
- char ebuf[1024];
- if (qemudGoDaemon() < 0) {
- VIR_ERROR(_("Failed to fork as daemon: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- goto error1;
- }
- }
-
- /* If running as root and no PID file is set, use the default */
- if (pid_file == NULL &&
- geteuid() == 0 &&
- REMOTE_PID_FILE[0] != '\0')
- pid_file = REMOTE_PID_FILE;
-
- /* If we have a pidfile set, claim it now, exiting if already taken */
- if (pid_file != NULL &&
- qemudWritePidFile (pid_file) < 0)
- goto error1;
-
- if (pipe(sigpipe) < 0 ||
- virSetNonBlock(sigpipe[0]) < 0 ||
- virSetNonBlock(sigpipe[1]) < 0 ||
- virSetCloseExec(sigpipe[0]) < 0 ||
- virSetCloseExec(sigpipe[1]) < 0) {
- char ebuf[1024];
- VIR_ERROR(_("Failed to create pipe: %s"),
- virStrerror(errno, ebuf, sizeof ebuf));
- goto error2;
- }
- sigwrite = sigpipe[1];
-
- sig_action.sa_sigaction = sig_handler;
- sig_action.sa_flags = SA_SIGINFO;
- sigemptyset(&sig_action.sa_mask);
-
- sigaction(SIGHUP, &sig_action, NULL);
- sigaction(SIGINT, &sig_action, NULL);
- sigaction(SIGQUIT, &sig_action, NULL);
- sigaction(SIGTERM, &sig_action, NULL);
- sigaction(SIGCHLD, &sig_action, NULL);
-
- sig_action.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sig_action, NULL);
-
- /* Ensure the rundir exists (on tmpfs on some systems) */
- if (geteuid() == 0) {
- const char *rundir = LOCAL_STATE_DIR "/run/libvirt";
-
- if (mkdir (rundir, 0755)) {
- if (errno != EEXIST) {
- VIR_ERROR0 (_("unable to create rundir"));
- return -1;
- }
- }
- }
-
- /* Beyond this point, nothing should rely on using
- * getuid/geteuid() == 0, for privilege level checks.
- * It must all use the flag 'server->privileged'
- * which is also passed into all libvirt stateful
- * drivers
- */
- if (qemudSetupPrivs() < 0)
- goto error2;
-
- if (!(server = qemudInitialize(sigpipe[0]))) {
- ret = 2;
- goto error2;
- }
-
- /* Read the config file (if it exists). */
- if (remoteReadConfigFile (server, remote_config_file) < 0)
- goto error2;
-
- /* Change the group ownership of /var/run/libvirt to unix_sock_gid */
- if (unix_sock_dir && server->privileged) {
- if (chown(unix_sock_dir, -1, unix_sock_gid) < 0)
- VIR_ERROR(_("Failed to change group ownership of %s"),
- unix_sock_dir);
- }
-
- if (virEventAddHandleImpl(sigpipe[0],
- VIR_EVENT_HANDLE_READABLE,
- qemudDispatchSignalEvent,
- server, NULL) < 0) {
- VIR_ERROR0(_("Failed to register callback for signal pipe"));
- ret = 3;
- goto error2;
- }
-
- if (!(server = qemudNetworkInit(server))) {
- ret = 2;
- goto error2;
- }
-
- qemudRunLoop(server);
-
- ret = 0;
-
-error2:
- if (server)
- qemudCleanup(server);
- if (pid_file)
- unlink (pid_file);
- close(sigwrite);
-
-error1:
- virLogShutdown();
- return ret;
-}
+++ /dev/null
-/*
- * qemud.h: daemon data structure definitions
- *
- * Copyright (C) 2006-2009 Red Hat, Inc.
- * Copyright (C) 2006 Daniel P. Berrange
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Author: Daniel P. Berrange <berrange@redhat.com>
- */
-
-
-#ifndef QEMUD_INTERNAL_H__
-#define QEMUD_INTERNAL_H__
-
-#include <config.h>
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include "gnutls_1_0_compat.h"
-#if HAVE_SASL
-#include <sasl/sasl.h>
-#endif
-
-#if HAVE_POLKIT0
-#include <dbus/dbus.h>
-#endif
-
-#ifdef HAVE_SYS_SYSLIMITS_H
-#include <sys/syslimits.h>
-#endif
-
-#include <rpc/types.h>
-#include <rpc/xdr.h>
-#include "remote_protocol.h"
-#include "logging.h"
-#include "threads.h"
-
-#ifdef __GNUC__
-#ifdef HAVE_ANSIDECL_H
-#include <ansidecl.h>
-#endif
-
-#ifndef __GNUC_PREREQ
-#if defined __GNUC__ && defined __GNUC_MINOR__
-# define __GNUC_PREREQ(maj, min) \
- ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
-#else
-#define __GNUC_PREREQ(maj,min) 0
-#endif
-#endif
-
-/**
- * ATTRIBUTE_UNUSED:
- *
- * Macro to flag conciously unused parameters to functions
- */
-#ifndef ATTRIBUTE_UNUSED
-#define ATTRIBUTE_UNUSED __attribute__((__unused__))
-#endif
-
-/**
- * ATTRIBUTE_FMT_PRINTF
- *
- * Macro used to check printf like functions, if compiling
- * with gcc.
- *
- * We use gnulib which guarentees we always have GNU style
- * printf format specifiers even on broken Win32 platforms
- * hence we have to force 'gnu_printf' for new GCC
- */
-#ifndef ATTRIBUTE_FMT_PRINTF
-#if __GNUC_PREREQ (4, 4)
-#define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (gnu_printf, fmtpos,argpos)))
-#else
-#define ATTRIBUTE_FMT_PRINTF(fmtpos,argpos) __attribute__((__format__ (printf, fmtpos,argpos)))
-#endif
-#endif
-
-#ifndef ATTRIBUTE_RETURN_CHECK
-#if __GNUC_PREREQ (3, 4)
-#define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__))
-#else
-#define ATTRIBUTE_RETURN_CHECK
-#endif
-#endif
-
-#else
-#ifndef ATTRIBUTE_UNUSED
-#define ATTRIBUTE_UNUSED
-#endif
-#ifndef ATTRIBUTE_FMT_PRINTF
-#define ATTRIBUTE_FMT_PRINTF(...)
-#endif
-#ifndef ATTRIBUTE_RETURN_CHECK
-#define ATTRIBUTE_RETURN_CHECK
-#endif
-#endif
-
-#define qemudDebug DEBUG
-
-/* Whether we're passing reads & writes through a sasl SSF */
-enum qemud_sasl_ssf {
- QEMUD_SASL_SSF_NONE = 0,
- QEMUD_SASL_SSF_READ = 1,
- QEMUD_SASL_SSF_WRITE = 2,
-};
-
-enum qemud_sock_type {
- QEMUD_SOCK_TYPE_UNIX = 0,
- QEMUD_SOCK_TYPE_TCP = 1,
- QEMUD_SOCK_TYPE_TLS = 2,
-};
-
-struct qemud_client_message {
- char buffer [REMOTE_MESSAGE_MAX + REMOTE_MESSAGE_HEADER_XDR_LEN];
- unsigned int bufferLength;
- unsigned int bufferOffset;
-
- int async : 1;
-
- remote_message_header hdr;
-
- struct qemud_client_message *next;
-};
-
-/* Allow for filtering of incoming messages to a custom
- * dispatch processing queue, instead of client->dx.
- */
-typedef int (*qemud_client_filter_func)(struct qemud_client_message *msg, void *opaque);
-struct qemud_client_filter {
- qemud_client_filter_func query;
- void *opaque;
-
- struct qemud_client_message *dx;
-
- struct qemud_client_filter *next;
-};
-
-/* Stores the per-client connection state */
-struct qemud_client {
- virMutex lock;
-
- int magic;
-
- int fd;
- int watch;
- int readonly:1;
- int closing:1;
-
- struct sockaddr_storage addr;
- socklen_t addrlen;
-
- int type; /* qemud_sock_type */
- gnutls_session_t tlssession;
- int auth;
- int handshake : 1; /* If we're in progress for TLS handshake */
-#if HAVE_SASL
- sasl_conn_t *saslconn;
- int saslSSF;
- const char *saslDecoded;
- unsigned int saslDecodedLength;
- unsigned int saslDecodedOffset;
- const char *saslEncoded;
- unsigned int saslEncodedLength;
- unsigned int saslEncodedOffset;
- char *saslUsername;
-#endif
-
- /* Count of meages in 'dx' or 'tx' queue
- * ie RPC calls in progress. Does not count
- * async events which are not used for
- * throttling calculations */
- int nrequests;
- /* Zero or one messages being received. Zero if
- * nrequests >= max_clients and throttling */
- struct qemud_client_message *rx;
- /* Zero or many messages waiting for a worker
- * to process them */
- struct qemud_client_message *dx;
- /* Zero or many messages waiting for transmit
- * back to client, including async events */
- struct qemud_client_message *tx;
- /* Filters to capture messages that would otherwise
- * end up on the 'dx' queue */
- struct qemud_client_filter *filters;
-
- /* This is only valid if a remote open call has been made on this
- * connection, otherwise it will be NULL. Also if remote close is
- * called, it will be set back to NULL if that succeeds.
- */
- virConnectPtr conn;
- int refs;
-
-};
-
-#define QEMUD_CLIENT_MAGIC 0x7788aaee
-
-
-struct qemud_socket {
- int fd;
- int watch;
- int readonly;
- int type; /* qemud_sock_type */
- int auth;
- int port;
- struct qemud_socket *next;
-};
-
-struct qemud_worker {
- pthread_t thread;
- int hasThread :1;
- int processingCall :1;
- int quitRequest : 1;
-
- /* back-pointer to our server */
- struct qemud_server *server;
-};
-
-/* Main server state */
-struct qemud_server {
- virMutex lock;
- virCond job;
-
- int privileged;
-
- int nworkers;
- int nactiveworkers;
- struct qemud_worker *workers;
- int nsockets;
- struct qemud_socket *sockets;
- int nclients;
- struct qemud_client **clients;
-
- int sigread;
- char *logDir;
- unsigned int shutdown : 1;
-#ifdef HAVE_AVAHI
- struct libvirtd_mdns *mdns;
-#endif
-#if HAVE_SASL
- char **saslUsernameWhitelist;
-#endif
-#if HAVE_POLKIT0
- DBusConnection *sysbus;
-#endif
-};
-
-void qemudLog(int priority, const char *fmt, ...)
- ATTRIBUTE_FMT_PRINTF(2,3);
-
-
-
-int qemudRegisterClientEvent(struct qemud_server *server,
- struct qemud_client *client);
-void qemudUpdateClientEvent(struct qemud_client *client);
-
-void qemudDispatchClientFailure(struct qemud_client *client);
-
-void
-qemudClientMessageQueuePush(struct qemud_client_message **queue,
- struct qemud_client_message *msg);
-struct qemud_client_message *
-qemudClientMessageQueueServe(struct qemud_client_message **queue);
-
-
-
-#if HAVE_POLKIT
-int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid);
-#endif
-
-#endif
#define __LIBVIRTD_REMOTE_H__
-#include "qemud.h"
+#include "libvirtd.h"
typedef union {
#include "remote_dispatch_args.h"